Подтвердить что ты не робот

Какие библиотеки зеленых потоков доступны для C, которые могут соответствовать производительности и простоте использования зеленых потоков Haskell?

Я хорошо привык полагаться на GHC forkIO для портативных легких потоков при программировании в Haskell.

Что такое эквивалентные библиотеки для C, которые могут обеспечить такую ​​же масштабируемость и простоту использования?

В частности, мне нужны С-эквиваленты, по крайней мере, следующих двух функций.

forkIO     :: IO () -> IO ThreadId
killThread ::             ThreadId -> IO ()

Я полагаю, что для моего приложения было бы достаточно, если потоки только включали блокирующие операции, а не принудительно приостанавливались, потому что все потоки часто очень часто блокируются для сетевого ввода-вывода, и я использую только системный вызов splice, чтобы спросить ядро ​​Linux перемещать данные между сокетами.


Обновление

В этой статье приведены цифры и таблицы, сравнивающие

с результатами в пользу Protothreads. Поскольку я не использовал их, и могут быть и другие библиотеки, мне бы очень хотелось услышать от тех, кто использовал/разработал такие библиотеки.

4b9b3361

Ответ 1

У меня больше нет комментариев к следующему коду, ни каких-либо примеров - это портативная (псевдо) библиотека потоков, реализованная как макросы предварительного процессора

     typedef struct
     {
     unsigned int magic;
     unsigned short ctx;
     unsigned char is_destroyed;
     }
     _run;

     typedef struct
     {
     unsigned int magic;
     unsigned int cnt;
     }
     _sem;


     #define aa_RUNNER_WAITING             0
     #define aa_RUNNER_YIELDED             1
     #define aa_RUNNER_EXITED              2
     #define aa_RUNNER_ENDED               3

     #define aaRunnerCreate(rp)            (rp)->magic='runr'; (rp)->ctx=0; (rp)->is_destroyed=NO
     #define aaRunnerDestroy(rp)           (rp)->is_destroyed=YES

     #define aaRunnerThread(args)          C args
     #define aaRunnerBegin(rp)             { C yflag=YES; if(yflag) {}  switch((rp)->ctx) { case 0:
     #define aaRunnerEnd(rp)               } yflag=NO; if(yflag) {}  aaRunnerCreate(rp); return aa_RUNNER_ENDED; }

     #define aaRunnerWaitUntil(rp,condx)   do  { (rp)->ctx=__LINE__; case __LINE__: if(!(condx))  { return aa_RUNNER_WAITING;  }  } while(0)
     #define aaRunnerWaitWhile(rp,condi)   aaRunnerWaitUntil((rp),!(condi))
     #define aaRunnerWaitThread(rp,thr)    aaRunnerWaitWhile((rp),aaRunnerSchedule(thr))
     #define aaRunnerWaitSpawn(rp,chl,thr) do { aaRunnerCreate((chl));  aaRunnerWaitThread((rp),(thr)); } while(0)

     #define aaRunnerRestart(rp)           do { aaRunnerCreate(rp); return aa_RUNNER_WAITING; } while(0)
     #define aaRunnerExit(rp)              do { aaRunnerCreate(rp); (rp)->magic=0; return aa_RUNNER_EXITED;  } while(0)

     #define aaRunnerSchedule(f)           ((f)<aa_RUNNER_EXITED)
     #define aaRunnerYield(rp)             do { yflag=NO; (rp)->ctx=__LINE__; case __LINE__: if(!yflag||!((rp)->is_destroyed))  { return aa_RUNNER_YIELDED;  }  } while(0)
     #define aaRunnerYieldUntil(rp,condi)  do { yflag=NO; (rp)->ctx=__LINE__; case __LINE__: if(!yflag||!(condi)) { return aa_RUNNER_YIELDED;   }   } while(0)

     #define aaRunnerSemInit(sp,c)         (sp)->magic='runs'; (sp)->cnt=c
     #define aaRunnerSemWait(rp,sp)        do { aaRunnerWaitUntil(rp,(sp)->cnt>0); --(sp)->cnt;  } while(0)
     #define aaRunnerSemSignal(rp,sp)      ++(sp)->cnt

Ответ 2

libMill - это то, что вы ищете: http://libmill.org/

Он реализует потоки уровня пользователя в стиле канала Go-Lang.

И он разрабатывается супер-умным Мартином Сустриком, создателем ZeroMQ http://250bpm.com/. Так что это должно быть хорошо ☺

Ответ 3

Используйте потоки POSIX. Они "зеленые" в любой современной реализации, а не в смысле "зеленых нитей", но в смысле легкого и эффективного. Нет никакого переносного способа перевернуть ваши собственные потоки поверх простых C или POSIX минус потоков. Как упоминалось в OP, есть несколько библиотек, реализующих "зеленые потоки" /совлокальные подпрограммы в непереносимом режиме (часто, несмотря на требования к переносимости).

Самый близкий к портативному подходу использует makecontext/swapcontext, и, к сожалению, это не может работать хорошо, потому что он должен делать системные вызовы для сохранения/восстановления маски сигнала на каждом коммутаторе между "потоками". Это делает переход между "зелеными" потоками дороже, чем переключение контекста между потоками уровня ядра в "реальной" реализации потоков POSIX и в основном отрицает любое заявленное преимущество "зеленых потоков".

Не переносимые подходы, которые не заботятся о сигнальной маске, могут использовать спецификацию asm для конкретного компьютера, чтобы полностью использовать контекстные переключатели в пользовательском пространстве, и теоретически выполнять лучше, чем потоки уровня ядра, но производительность снова выйдет из окна как только вы вводите IO, так как поток, который собирается выполнить IO, должен сначала выполнить дорогостоящие тесты, чтобы проверить, будет ли операция блокироваться, и если это так, переведите управление в другой поток.

Я утверждаю свою позицию, что "зеленые потоки" - это идея, чье время давно прошло. Это также похоже на позицию Austin Group (отвечающей за POSIX), которая удалила функции ucontext в POSIX 2008 и рекомендовала замену потоками POSIX (которые теперь являются обязательной функцией).