14

将 Windows 手动重置事件移植到 pthread 是否有比 pthread 条件变量 + pthread 互斥体 + 如果设置或未设置事件的标志更简单的解决方案?

4

7 回答 7

18

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,但这是它通常使用方式的要点。

于 2008-10-07T15:15:28.900 回答
7

You can easy implement manual-reset events with pipes:

event is in triggered state -> there is something to read from the pipe

SetEvent -> write()

ResetEvent -> read()

WaitForMultipleObjects -> poll() (or select()) for reading

the "SetEvent" operation should write something (e.g. 1 byte of any value) just to put the pipe in non-empty state, so subsequent "Wait" operation, that is, poll() for data available for read will not block.

The "ResetEvent" operation will read up the data written to make sure pipe is empty again. The read-end of the pipe should be made non-blocking so that trying to reset (read from) already reset event (empty pipe) wont block - fcntl(pipe_out, F_SETFL, O_NONBLOCK) Since there may be more than 1 SetEvents before the ResetEvent, you should code it so that it reads as many bytes as there are in the pipe:

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

Note that waiting for the event does not read from the pipe and hence the "event" will remain in triggered state until the reset operation.

于 2012-04-12T01:41:08.843 回答
5

我更喜欢管道方法,因为通常不仅需要等待一个事件,还需要多个对象,例如WaitForMultipleObjects(...). 并且使用管道可以轻松地将 windowsWaitForMultipleObjects调用替换为poll(...), select,pselectepoll

有一种轻量级的进程同步方法,称为Futex(快速用户空间锁定系统调用)。有一个函数futex_fd可以为 futex 获取一个或多个文件描述符。该文件描述符以及可能代表真实文件、设备、套接字等的许多其他文件描述符可以传递给selectpollepoll. 不幸的是,它已从内核中删除。所以管道技巧仍然是唯一可以做到这一点的工具:

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]);
于 2012-06-29T11:22:32.050 回答
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);
}

调用 signal() 时,事件进入有信号状态,所有等待线程都将运行。然后事件将保持在信号状态,所有调用 wait() 的线程都不会等待。对 reset() 的调用将使事件恢复到非信号状态。

如果您执行快速信号/重置以唤醒等待的线程,则 signalCounter 就在那里。

于 2012-02-28T21:40:00.767 回答
2

我认为 Windows 事件更类似于信号量。即对于自动重置,您将使用二进制信号量和 sem_timedwait() 函数。

于 2009-10-06T07:30:14.087 回答
2

我们一直在寻找类似的解决方案,将一些重度多线程 C++ 代码从 Windows 移植到 Linux,并最终编写了一个开源的、MIT 许可的 Linux 事件库。它应该是您正在寻找的解决方案,并且已经过严格的性能和资源消耗审查。

它实现了手动和自动重置事件,以及WaitForSingleObjectWaitForMultipleObject功能。

于 2013-01-22T14:29:03.107 回答
0

我们(完全披露:我在 NeoSmart Technologies 工作)编写了一个名为pevents的开源(MIT 许可)库,它在 POSIX 上实现 WIN32 手动和自动重置事件,并包括 WaitForSingleObject 和 WaitForMultipleObjects 克隆。从那时起,它就得到了一些采用(它在 Linux/Mac 上的 Steam 中使用)并且运行良好。

虽然我个人建议您在 POSIX 机器上编码时使用 POSIX 多线程和信号范式,但如果您需要,pevents 为您提供了另一种选择。

于 2015-07-11T18:36:51.503 回答