Портирование события ручного сброса Windows на Linux? - PullRequest
11 голосов
/ 07 октября 2008

Есть ли более простое решение для переноса события ручного сброса Windows на pthread, чем условная переменная pthread + мьютекс pthread + флаг, если событие установлено или не установлено?

Ответы [ 7 ]

17 голосов
/ 07 октября 2008

Pthreads - конструкции низкого уровня. Нет, не существует более простого механизма; pthread_cond__* концептуально аналогично событию автоматического сброса. Будьте осторожны, pthread_cond_wait может иметь ложные пробуждения, поэтому его никогда не следует использовать без какого-либо внешнего флага, независимо от ситуации.

Построить свой не было бы слишком сложно.

#include <pthread.h>
#include <stdbool.h>

struct mrevent {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool triggered;
};

void mrevent_init(struct mrevent *ev) {
    pthread_mutex_init(&ev->mutex, 0);
    pthread_cond_init(&ev->cond, 0);
    ev->triggered = false;
}

void mrevent_trigger(struct mrevent *ev) {
    pthread_mutex_lock(&ev->mutex);
    ev->triggered = true;
    pthread_cond_signal(&ev->cond);
    pthread_mutex_unlock(&ev->mutex);
}

void mrevent_reset(struct mrevent *ev) {
    pthread_mutex_lock(&ev->mutex);
    ev->triggered = false;
    pthread_mutex_unlock(&ev->mutex);
}

void mrevent_wait(struct mrevent *ev) {
     pthread_mutex_lock(&ev->mutex);
     while (!ev->triggered)
         pthread_cond_wait(&ev->cond, &ev->mutex);
     pthread_mutex_unlock(&ev->mutex);
}

Это может не соответствовать вашему использованию, так как у вас часто будет другая блокировка, которую вы хотели бы использовать вместо ev->mutex, но это суть того, как она обычно используется.

7 голосов
/ 12 апреля 2012

Вы можете легко реализовать события ручного сброса с помощью каналов:

событие в запущенном состоянии -> есть что прочитать из канала

SetEvent -> write ()

ResetEvent -> read ()

WaitForMultipleObjects -> poll () (или select ()) для чтения

операция «SetEvent» должна записать что-то (например, 1 байт любого значения), чтобы перевести канал в непустое состояние, поэтому последующая операция «Wait», то есть poll () для данных, доступных для чтения, не будет блок.

Операция «ResetEvent» считывает записанные данные, чтобы убедиться, что канал снова пуст. Конец чтения канала должен быть неблокирующим, чтобы попытка сброса (чтение из) уже сброшенного события (пустой канал) не блокировалась - fcntl (pipe_out, F_SETFL, O_NONBLOCK) Поскольку перед ResetEvent может быть более 1 SetEvents, вы должны закодировать его так, чтобы он читал столько байтов, сколько имеется в канале:

char buf[256]; // 256 is arbitrary
while( read(pipe_out, buf, sizeof(buf)) == sizeof(buf));

Обратите внимание, что ожидание события не считывается из канала, и, следовательно, «событие» останется в сработавшем состоянии до операции сброса.

5 голосов
/ 29 июня 2012

Я предпочитаю конвейерный подход, потому что часто нужно не просто событие, которое нужно ждать, а несколько объектов, например WaitForMultipleObjects(...). А с помощью каналов можно легко заменить вызов Windows WaitForMultipleObjects на poll(...), select, pselect и epoll.

Был упрощенный метод для синхронизации процессов, который называется Futex (системный вызов Fast Userspace Locking). Была функция futex_fd, чтобы получить один или несколько файловых дескрипторов для фьютексов. Этот дескриптор файла вместе с, возможно, многими другими, представляющими реальные файлы, устройства, сокеты или тому подобное, может быть передан в select, poll или epoll. К сожалению, было удалено из ядра. Таким образом, уловки с трубами остаются единственной возможностью сделать это:

int pipefd[2];
char buf[256]; // 256 is arbitrary
int r = pipe2(pipefd, O_NONBLOCK);

void setEvent()
{
  write(pipefd[1], &buf, 1); 
}

void resetEvent() {  while( read(pipefd[0], &buf, sizeof(buf)) > 0 ) {;} }

void waitForEvent(int timeoutMS)
{ 
   struct pollfd fds[1];
   fds[0].fd = pipefd[0];
   fds[0].events = POLLRDNORM;
   poll(fds, 1, timeoutMS);
}

// finalize:
close(pipefd[0]);
close(pipefd[1]);
3 голосов
/ 29 февраля 2012

Нет, нет более простого решения, но следующий код поможет:

void LinuxEvent::wait()
{
    pthread_mutex_lock(&mutex);

    int signalValue = signalCounter;

    while (!signaled && signalValue == signalCounter)
    {
        pthread_cond_wait(&condition, &mutex);
    }

    pthread_mutex_unlock(&mutex);
}

void LinuxEvent::signal()
{
    pthread_mutex_lock(&mutex);

    signaled = true;
    signalCounter++;
    pthread_cond_broadcast(&condition);

    pthread_mutex_unlock(&mutex);
}

void LinuxEvent::reset()
{
    pthread_mutex_lock(&mutex);
    signaled = false;
    pthread_mutex_unlock(&mutex);
}

При вызове signal () событие переходит в сигнальное состояние и запускается весь ожидающий поток. Тогда событие останется в сигнальном состоянии, и весь поток, вызывающий wait (), не будет ждать. Вызов reset () вернет событие в состояние без сигнала.

Сигнальный счетчик существует, если вы делаете быстрый сигнал / сброс для пробуждения ожидающих потоков.

2 голосов
/ 22 января 2013

Мы искали аналогичное решение для переноса сильно многопоточного кода C ++ с Windows на Linux, и в итоге мы написали MIT Win32 Events для Linux библиотеки с открытым исходным кодом . Это должно быть решение, которое вы ищете, и было тщательно проверено на производительность и потребление ресурсов.

Он реализует события ручного и автоматического сброса, а также функции WaitForSingleObject и WaitForMultipleObject.

2 голосов
/ 06 октября 2009

Я думаю, что Windows Events больше похожи на семафор. То есть для автоматического сброса вы должны использовать двоичный семафор и функцию sem_timedwait ().

0 голосов
/ 11 июля 2015

Мы (полное раскрытие: я работаю в NeoSmart Technologies) написали библиотеку с открытым исходным кодом (по лицензии MIT) под названием pevents , которая реализует события ручного и автоматического сброса WIN32 в POSIX, и включает клоны WaitForSingleObject и WaitForMultipleObjects. С тех пор он получил некоторое распространение (он используется в Steam на Linux / Mac) и работает довольно хорошо.

Хотя я лично советую вам использовать парадигмы многопоточности и сигнализации POSIX при кодировании на машинах POSIX, pevents дает вам другой выбор, если вам это нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...