вторник, 27 апреля 2010 г.

Какой аналог у SetEvent (Win32) в pthread?

Материал взят из http://electronix.ru:1288/forum/lofiversion/index.php/t47683.html

Andrey Sudnov
May 13 2008, 11:36
Проблема в том, что в библиотеке PThread нет работы с файловыми дескрипторами. Соответственно, для того чтобы одновременно ожидать завершения какой-либо файловой (или сокетовой) операции или поступления управляющего сигнала от другого потока (например, об отмене операции), приходится использовать неименнованные каналы (pipe и write, вместо SetEvent) и функцию poll (предпочитаю ее, а не select). Перерыл кучу книг, в том числе POSIX стандарт, ничего лучше не придумал. Насколько такое решение накладно по ресурсам? Как организовать такое взаимодействие потоков другим способом?
KRS
May 14 2008, 11:38
в POSIX есть mutex и Condition Variables
я когда игрался с POSIX под сигвин делал примерно так:

typedef struct {
pthread_mutex_t lock;
pthread_cond_t event;
bool flag;
}event_t;

static inline void event_init(event_t* event)
{
pthread_mutex_init(&event- >lock,0);
pthread_cond_init(&event- >event,0);
event- >flag=false;
}

static inline void event_set(event_t* event)
{
pthread_mutex_lock(&event- >lock);
if (!event- >flag) {
event- >flag=true;
pthread_cond_signal(&event- >event);
}
pthread_mutex_unlock(&event- >lock);
}

static inline bool event_wait(event_t* event, unsigned s_timeout)
{
bool r;
pthread_mutex_lock(&event- >lock);
if(!event- >flag) {
if (s_timeout) {
timespec_t timer;
clock_gettime(CLOCK_REALTIME,&timer);
timer.tv_sec+=s_timeout;
pthread_cond_timedwait(&event- >event,&event- >lock,&timer);
}else {
do {
pthread_cond_wait(&event- >event,&event- >lock);
}while(!event- >flag);
}
}
r=event- >flag;
event- >flag=false;
pthread_mutex_unlock(&event- >lock);
return r;
}
RCray
May 15 2008, 12:05
да. это даже лучше, чем poll, который не для всех устройств реализован. к тому же в вашем коде подчерпнул кое-что для себя, спасибо.
RCray
May 15 2008, 13:23
только у меня так:


void* child_func(void* arg)
{
struct args_t* args = (args*)arg;

args- >size = read(args- >fd, args- >packet, args- >supposed_size);
args- >event.flag = 1;
pthread_cond_signal(&args- >event.event);

return 0;
}

int parent(struct args_t *args)
{
pthread_t child;
struct timespec tm;

if ( pthread_create(&child, NULL, child_func, args) != 0 )
return ERR_CREATE;

// задание таймаута
...

while(!args- >event.flag)
{
res = pthread_cond_timedwait(&args- >event.event, &args- >event.lock, &tm);
if (res == ETIMEDOUT)
return ERR_TIMEOUT;
}

args- >event.flag = 0;
return args- >errors;
}

KRS
May 15 2008, 15:31
Без mutex есть потенциальные проблемы:
если между проверкой и очисткой возникнет еще одно событие вы его потеряете потому что обнулите args- >event.flag = 0;

(2b|!2b?.. @ May 15 2008, 14:08) [snapback]411551[/snapback]


while(!args- >event.flag)
{
res = pthread_cond_timedwait(&args- >event.event, &args- >event.lock, &tm);
if (res == ETIMEDOUT)
return ERR_TIMEOUT;
}

args- >event.flag = 0;
return args- >errors;
}



RCray
May 15 2008, 15:59
(KRS @ May 15 2008, 16:16) [snapback]411655[/snapback]

Без mutex есть потенциальные проблемы:
если между проверкой и очисткой возникнет еще одно событие вы его потеряете потому что обнулите args- >event.flag = 0;


да, опять вы правы.
к тому же не лишним будет это

res = pthread_cond_timedwait(&args- >event.event, &args- >event.lock, &tm);
if (res == ETIMEDOUT)
{
pthread_cancel(созданный ранее поток, от которого ожидается сигнал);
return ERR_TIMEOUT;
}

Andrey Sudnov
May 18 2008, 07:14
Не нахожу ответа на свой вопрос... Уточняю условие.
Можно использовать poll, можно select, разницы нет, дело вкуса. Проблема в том, что БЕЗ этих функций невозможно отследить окончание операции с файлом (read, write, recv, send, conect, etc...).
Ситуация следующая. Есть два потока. Один - интерфейс, и там есть кнопка Exit. Другой - работа с сокетами. Работа с сокетами должна быть немедленно прекращена, как только нажали кнопку Exit. Для этого необходимо ждать одновременно и файловый дескриптор (poll/select) и event от pthread (если мы работаем через них). Это невозможно - нет такой функции. В Windows есть такой тип: HANDLE. Он общий и для событий (SetEvent) и для дескрипторов ввода/вывода. Соответственно, оба можно одновременно передать в функцию WaitForMultipleObjects.
Вижу два варианта. Первый: вызывать poll/select с установленным таймаутом в несколько миллисекунд, затем проверять событие и так по кругу. С кнопкой это конечно прокатит, но в случае жесткого реального времени задержка на обработку события будет составлять именно этот таймаут. К тому же постоянное верчение в юзеровском коде не делает чести с точки зрения использования ресурсов и производительности.
Второй вариант: отказаться от синхронизирующих функций pthread нафиг. Использовать pipe. Здесь встает вопрос, насколько эффективно реализованы эти каналы в коде ОС/libc. И не перекрывает ли потенциальная кривая реализация преимуществ над первым вариантом?
Варианты еще?
vshemm
May 18 2008, 17:31
(Andrey Sudnov @ May 18 2008, 07:59) [snapback]412873[/snapback]

Второй вариант: отказаться от синхронизирующих функций pthread нафиг. Использовать pipe. Здесь встает вопрос, насколько эффективно реализованы эти каналы в коде ОС/libc. И не перекрывает ли потенциальная кривая реализация преимуществ над первым вариантом?

А ОС, собственно, какая?
Впрочем, если не нравятся пайпы, можно использовать сокеты smile.gif Для них есть высокоэффективная реализация оповещения об изменении состояния, правда, механизмы в разных ОС разные. Гляньте сюда - http://monkey.org/~provos/libevent/
Так или иначе, задержки будут намного меньше таймаута в неск. миллисекунд при небольшом количестве сокетов.

Варианты еще?

Сигналы?

А вот что пишет Steven Grimm (http://monkeymail.org/archives/libevent-users/2007-January/000450.html):

no UNIX-ish system I'm aware of has an equivalent to the Windows
WaitForMultipleObjects API that allows you to wake up on semaphores /
condition variables and on input from the network. Without that, any
solution is going to end up using pipes (or maybe signals, which have
their own set of issues in a multithreaded context) to wake up the
libevent threads.

однако, про локальные сокеты он почему-то не упоминает.

И еще smile.gif Вы зря,имхо, предпочитаете poll select'у, т.к. poll при каждом вызове гоняет структу в ядро = > большой оверхед. С другой стороны селект имеет ограничени на кол-во дескрипторов. Во всяком случае в linux это так, поэтому и появился epoll().

Комментариев нет:

Отправить комментарий

Постоянные читатели

Архив блога