Ответ 1
Если вы действительно хотите сделать что-то вроде функций ucontext.h
, я бы продолжал их использовать. Все остальное будет менее переносимым. Пометка их устаревания в POSIX, кажется, была ужасной ошибкой педантизма кем-то в комитете. Сам POSIX требует, чтобы указатели на указатели и указатели данных были одинакового размера, а указатели функций представлялись литыми в void *
, а сам C сам требовал отбрасывания между типами указателей функций и обратно, чтобы быть безопасным в обоих направлениях, поэтому существует много способов этот вопрос можно было бы решить.
Существует одна реальная проблема, заключающаяся в том, что преобразование int argc, ...
, переданного в makecontext
в форму для перехода к функции, не может быть выполнено без значительной помощи от компилятора, если только вызывающее соглашение для вариативных и невариантных функций не происходит быть одинаковым (и даже тогда довольно сомнительно, можно ли это сделать надежно). Однако эту проблему можно было решить просто путем уменьшения использования makecontext
в любой форме, кроме makecontext(ucp, func, 1, (void *)arg);
.
Возможно, лучший вопрос, почему вы думаете, что функции ucontext.h
- лучший способ обработки потоков. Если вы хотите пойти с ними, я мог бы предложить написать интерфейс оболочки, который можно реализовать либо с помощью ucontext.h
, либо с помощью pthreads, а затем сравнить производительность и раздувание. Это также будет иметь то преимущество, что, если будущие системы откажутся от поддержки ucontext.h
, вы можете просто переключиться на компиляцию с реализацией на основе pthread, и все будет просто работать. (К тому времени раздувание может быть менее важным, преимущество многоядерных /SMP, вероятно, будет огромным, и, надеюсь, реализация pthread будет менее раздутой.)
Изменить (на основе запроса OP): Чтобы реализовать "совместную потоковую обработку" с помощью pthreads, вам нужны переменные условия. Здесь вы найдете достойный учебник pthreads с информацией об их использовании:
https://computing.llnl.gov/tutorials/pthreads/#ConditionVariables
Ваш совлокальный многозадачный примитив "hand off execution to thread X" будет выглядеть примерно так:
self->flag = 0;
other_thread->flag = 1;
pthread_mutex_lock(other_thread->mutex);
pthread_cond_signal(other_thread->cond);
pthread_mutex_unlock(other_thread->mutex);
pthread_mutex_lock(self->mutex);
while (!self->flag)
pthread_cond_wait(self->cond, self->mutex);
pthread_mutex_unlock(self->mutex);
Надеюсь, я все понял; по крайней мере, общая идея верна. Если кто-то видит ошибки, прокомментируйте, чтобы я мог это исправить. Половина блокировки (other_thread
мьютекса), вероятно, совершенно не нужна при таком использовании, поэтому вы могли бы сделать мьютекс локальной переменной в функции task_switch
. Все, что вы действительно делаете, это использовать pthread_cond_wait
и pthread_cond_signal
как примитивы "идти спать" и "пробуждать другие потоки".