> можно пример кода, который бы одновременно ожидал три события на трех условных переменных?
int x,y,z;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
...
pthread_mutex_lock(&mut);
while ( !(x==VALUE_X && y==VALUE_Y && z==VALUE_Z) ) {
pthread_cond_wait(&cond, &mut);
}
/* Do something with x,y,z */
pthread_mutex_unlock(&mut);
/* Another thread(s) */
pthread_mutex_lock(&mut);
/* modify x,y,z */
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
Это простейшая схема (слегка модифицировано из man pthread_cond_init).
Логика работы не совсем такая, как у WaitForMultipleObjects,
но при желании можно приблизить.
> ps: я даже не ставлю вполне естественного дополнения a'la "а ещё
> плюс на сокете, мьютексе и семафоре".
В виду произвольности условия ждать можно на чем угодно, хоть на
кнопке в GUI тулките.
> Ну и где здесь ожидание 2-х условных переменных, что требовалось по условию задачи?
В таком случае, что есть условная переменная или condvar, по вашему? Нашел по condvar вот это: http://bama.ua.edu/cgi-bin/man-cgi?condvar+9F интерфейс --- один-в-один pthread_cond_*.
В приведенном примере условными являются переменные x,y,z.
> Ну так приведи пример ожидания на condvar и сокете.
Ну, замени в примере условие для переменной вызовом poll без ожидания.
> В таком случае, что есть условная переменная или condvar, по вашему? Нашел по condvar вот это: http://bama.ua.edu/cgi-bin/man-cgi?condvar+9F интерфейс --- один-в-один pthread_cond_*.
пусть есть два независимых модуля A и B и наш поток P. модули экспортируют объекты синхронизации. задача потока P дождаться первого доступного события от модулей, обработать его и так по циклу.
в случае с Win32 все прозрачно: модули создают каждый по своему событию через CreateEvent() и поток P ждет на них через WaitForMultipleObjects(). замечу, ждет одновременно на двух независимых друг от друга объектах синхронизации. модулю оповещают поток о событии через вызов SetEvent().
предложите реализацию аналогичной схемы для мира POSIX без привлечения select() :)
> что есть условная переменная или condvar, по вашему?
"По нашему" :'-( Парень, это POSIX...
>> Ну так приведи пример ожидания на condvar и сокете.
>Ну, замени в примере условие для переменной вызовом poll без ожидания.
Не катит. Я могу зависнуть на condvar, и не среагирую на приход данных из сокета. Если делать ожидание с тайм-аутом, то это качественно не отличается от busy wait loop.
> пусть есть два независимых модуля A и B и наш поток P. модули экспортируют объекты синхронизации. задача потока P дождаться первого доступного события от модулей, обработать его и так по циклу.
На "чистых" condvar'ах такое не сделаешь. Но, как мне кажется, вещи типа OS/2-ного muxwait в POSIX делаются довольно просто. Где-то я читал, что pthreads предоставляют механизмы для реализации ваших собственных примитивов, а Win32 - уже реализованные ваши примитивы.
> На "чистых" condvar'ах такое не сделаешь. Но, как мне кажется, вещи типа OS/2-ного muxwait в POSIX делаются довольно просто. Где-то я читал, что pthreads предоставляют механизмы для реализации ваших собственных примитивов, а Win32 - уже реализованные ваши примитивы.
жду примера реализации :) неплохой кстати challenge.
> жду примера реализации :) неплохой кстати challenge.
Я ждал "нет, это невозможно по <списокпричин>", или "боянЪ - давно реализовано". А насчет интерфейса модулей A и B - этот интерфейс можно сделать состоящим из указателей на mutex и condvar, и проблема решена.
> Я ждал "нет, это невозможно по <списокпричин>", или "боянЪ - давно реализовано". А насчет интерфейса модулей A и B - этот интерфейс можно сделать состоящим из указателей на mutex и condvar, и проблема решена.
ну если задача вида "нативный демультиплексор событий от объектов POSIX" то AFAIU задача не решена бо нативный UNIX API не вяжется с API pthread. если задача вида "демультиплексор событий от высокоуровневых объектов на базе POSIX примитивов" то конечно же решена, как пример - тот-же ACE framework.
> "Нить" - это в смысле thread :-P
назовем хоть паутинкой но суть от этого не поменяется и challenge состоит отнюдь не в выяснении лексических корней перевода thread.
Под задачей имелась в виду задача вроде приведенной вами в этом треде.
> ну если задача вида "нативный демультиплексор событий от объектов POSIX" то AFAIU задача не решена бо нативный UNIX API не вяжется с API pthread
Для "нативного" решения этой задачи нужно модифицировать ядро.
> challenge состоит отнюдь не в выяснении лексических корней перевода thread.
Я собираюсь реализовать нечто очень похожее, но не смогу опубликовать код, так что вызов не принят. Но идея банальная: пока в muxwait все события от нитей, используются примитивы pthreads, а когда надо ждать на смеси объектов, используется poll.
> Я собираюсь реализовать нечто очень похожее, но не смогу опубликовать код, так что вызов не принят. Но идея банальная: пока в muxwait все события от нитей, используются примитивы pthreads, а когда надо ждать на смеси объектов, используется poll.
...и именно в последнем случае всплывает оригинальный вопрос: как вы будете ждать одновременно на сокете и мьютексе. очень желательно без привлечения тяжелой артилерии :)
> сплывает оригинальный вопрос: как вы будете ждать одновременно на сокете и мьютексе.
Я буду жлать либо на сокете, либо на condvar, но не одновременно (естественно). Но, если muxwait является смесью объектов, то операция "ждать" является poll, а операция "просигналить событие" пишет байт в специально созданный для внутреннего использования объектом muxwait pipe или сокет (который тоже передается в poll).
> очень желательно без привлечения тяжелой артилерии :)
А "тяжелая артиллерия" - это что? Если poll, то что в нем такого тяжелого?
> Я буду жлать либо на сокете, либо на condvar, но не одновременно (естественно). Но, если muxwait является смесью объектов, то операция "ждать" является poll, а операция "просигналить событие" пишет байт в специально созданный для внутреннего использования объектом muxwait pipe или сокет (который тоже передается в poll).
ok
> А "тяжелая артиллерия" - это что? Если poll, то что в нем такого тяжелого?
пример в виде дополнительного socket pair 1:1 для связки разнородных IPC - это и есть тяжелая артилерия :)
> По-другому никак не придумал. А что такого тяжелого в socketpair или pipe? Память ядра для буфера?
как минимум - два дополнительных файловых дескриптора на каждый гибридный канал IPC (а если их 1000+?) + AFAIU неоднородные механизмы диспетчеризации потоков в случае нативных средств синхронизации pthread vs "тяжелых" aka poll(2). ну и в фоне - накладные расходы на ведение всей этой кухни.
> два дополнительных файловых дескриптора на каждый гибридный канал IPC (а если их 1000+?)
файловый дескриптор - вещь дешевая и почти неограниченная (30000+), да предполагаемый сценарий использования не предусматривает создания сотен muxwait. ИМХО вообще будет по muxwait'у на нить, а по сравнению с 1000 нитей расходы на дескрипторы - ерунда.
> AFAIU неоднородные механизмы диспетчеризации потоков в случае нативных средств синхронизации pthread vs "тяжелых" aka poll(2)
только время покажет
> накладные расходы на ведение всей этой кухни.
в runtime это будут копейки - один системный вызов на сигнализацию события, вряд ли даже pthread без него обходится.
> Кстати, а ядерный kqueue(2) из FreeBSD случайно не делает именно то, что нужно?
kqueue в *BSD (уже не только Free) - вещь несомненно интересная и полезная, но есть конечно же пара но:
1) я не уверен, что kqueue интегрируется с POSIX thread aka возможно ожидание события от условной переменной, семафора или мьютекса. банально базируясь на предположении о том, что имея модель потоков N:M и, как следствие, отчасти реализацию потоков в user land, это технически корректно осуществимо [а тем более сделать это просто].
2) опять же вопрос работы планировщика -> потенциальные дедлоки Бог знает где -> может быть много проблем..
99) ну и совместимость опять же. хотя, если использовать врапер то это уже не так важно.