Перенос события ручного сброса Windows в Linux?

#windows #linux #multithreading #pthreads

Вопрос:

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

Ответ №1:

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(amp;ev->mutex, 0);
    pthread_cond_init(amp;ev->cond, 0);
    ev->triggered = false;
}

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

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

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

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

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

1. Не забывайте, что событие автоматического сброса Windows «запомнит», что оно было сигнализировано, и сообщит следующему потоку, который ожидает, а затем сбросит себя. сигнал pthread_cond_signal может фактически ничего не делать, если нет ожидающих потоков, поэтому «событие» в этом случае, по-видимому, не произошло.

2. @эфемерный: «Функции pthread_cond_wait() и pthread_cond_timedwait() используются для блокировки переменной условия. Они вызываются с помощью мьютекса, заблокированного вызывающим потоком, или в результате возникнет неопределенное поведение». Разве вы не должны фиксировать мьютекс перед каждым вызовом pthread_cond_wait? Эти функции атомарно освобождают мьютекс и заставляют вызывающий поток блокировать переменную условия cond;

3. @user877329: Функции pthread_cond_* отбрасывают мьютекс во время ожидания и повторно запрашивают его перед возвращением.

4. @эфемерный отличный ответ, чувак. Для меня это сработало как заклинание. определенно 1. Я бы принял ответ, если бы задал его. Я не знаю, что не так с sfhdhsf, так как это прекрасно отвечает на его вопрос 🙂

Ответ №2:

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

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

SetEvent -> запись()

Сбросить событие -> прочитать()

WaitForMultipleObjects -> опрос() (или выберите()) для чтения

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

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

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

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

Ответ №3:

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

Существовал легкий метод синхронизации процессов под названием Futex (системный вызов быстрой блокировки пользовательского пространства). Существовала функция 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], amp;buf, 1); 
}

void resetEvent() {  while( read(pipefd[0], amp;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]);
 

Ответ №4:

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

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

    int signalValue = signalCounter;

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

    pthread_mutex_unlock(amp;mutex);
}

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

    signaled = true;
    signalCounter  ;
    pthread_cond_broadcast(amp;condition);

    pthread_mutex_unlock(amp;mutex);
}

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

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

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

Ответ №5:

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

Ответ №6:

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

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

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

1. Это выглядит очень мило. Жаль, что дескрипторы *nix, семафоры, события, потоки, сокеты и т. Д. Не доступны для ожидания единым способом, как в Windows, где один вызов управляет ими всеми.

Ответ №7:

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

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