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

Pthread-like windows manual- reset событие

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

4b9b3361

Ответ 1

Pthreads - это конструкции низкого уровня. Нет, нет более простого механизма; pthread_cond__* концептуально похож на событие auto- reset. Будьте осторожны, 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, но это суть того, как она обычно используется.

Ответ 2

Вы можете легко выполнить события manual- reset с трубами:

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

SetEvent → write()

ResetEvent → read()

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

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

Операция "ResetEvent" будет считывать записанные данные, чтобы убедиться, что канал снова пуст. Чтение конца трубы должно быть сделано без блокировки, так что попытка reset (читать) уже reset событие (пустой канал) не будет блокировать - 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));

Обратите внимание, что ожидание события не считывается из канала, и, следовательно, "событие" останется в запущенном состоянии до операции reset.

Ответ 3

Нет никакого простого решения, но следующий код выполнит трюк:

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);
}

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

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

Ответ 4

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

Был применен легкий метод для синхронизации процесса, называемый Futex (системный вызов Fast Userspace Locking). Была функция futex_fd, чтобы получить один или несколько дескрипторов файлов для futexes. Этот файловый дескриптор вместе с возможно многими другими, представляющими реальные файлы, устройства, сокеты или тому подобное, может быть передан в 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]);

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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