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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
13
0
16 012
7

Ответы 7

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, но это суть того, как он обычно используется.

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

ScaryAardvark 08.03.2010 14:54

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

user877329 10.06.2014 23:11

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

ephemient 11.06.2014 05:29

@ephemient отличный ответ человек. Сработало для меня как шарм. определенно +1. Я бы принял ответ, если бы спросил. Я не знаю, что не так с sfhdhsf, поскольку это отлично отвечает на его вопрос :)

Pankaj Bansal 18.07.2015 08:42

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

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

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 () вернет событие в несигнальное состояние.

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

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

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

SetEvent -> написать ()

ResetEvent -> читать ()

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

операция «SetEvent» должна записать что-то (например, 1 байт любого значения) только для того, чтобы перевести канал в непустое состояние, поэтому последующая операция «Ожидание», то есть 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));

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

Я предпочитаю конвейерный подход, потому что часто нужно ждать не просто события, а нескольких объектов, например. 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]);

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

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

Выглядит очень красиво. Его слишком плохие дескрипторы * nix, семафоры, события, потоки, сокеты и т. д. Не ждут единообразно, как в Windows, где ими управляет один вызов.

jww 11.02.2014 17:45

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

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

Другие вопросы по теме