将windows手动重置事件移植到Linux?

时间:2008-10-07 11:55:39

标签: windows linux multithreading pthreads

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

7 个答案:

答案 0 :(得分:17)

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

答案 1 :(得分:7)

您可以使用管道轻松实现手动重置事件:

事件处于触发状态 - &gt;有一些东西可以从管道中读取

SetEvent - &gt;写()

ResetEvent - &gt;读()

WaitForMultipleObjects - &gt; poll()(或select())用于阅读

“SetEvent”操作应该写一些东西(例如任何值的1个字节)只是为了将管道置于非空状态,因此后续的“等待”操作,即poll()可用于读取的数据不会块。

“ResetEvent”操作将读取写入的数据以确保管道再次为空。 管道的读取端应该是非阻塞的,以便尝试重置(读取)已经重置的事件(空管道)不会阻塞 - fcntl(pipe_out,F_SETFL,O_NONBLOCK) 由于在ResetEvent之前可能有多个SetEvent,因此您应该对其进行编码,使其读取与管道中的字节数一样多的字节:

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

请注意,等待事件不会从管道读取,因此“事件”将保持触发状态,直到重置操作。

答案 2 :(得分:5)

我更喜欢管道方法,因为通常一个人不仅需要等待一个事件,而是多个对象,例如WaitForMultipleObjects(...)。使用管道时,可以使用WaitForMultipleObjectspoll(...)selectpselect轻松替换Windows epoll来电。

有一种称为Futex(快速用户空间锁定系统调用)的轻量级进程同步方法。有一个函数futex_fd来获取futexes的一个或多个文件描述符。该文件描述符以及可能代表真实文件,设备,套接字等的许多其他文件描述符可以传递给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]);

答案 3 :(得分: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就在那里。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

我们一直在寻找一种类似的解决方案,将一些重度多线程的C ++代码从Windows移植到Linux,最终编写了一个开源的,麻省理工学院授权的Win32 Events for Linux library。它应该是您正在寻找的解决方案,并且已经过严格的性能和资源消耗审查。

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

答案 6 :(得分:0)

我们(完全披露:我在NeoSmart Technologies工作)编写了一个名为pevents的开源(MIT许可)库,其implements WIN32 manual and auto-reset events位于POSIX上,并包含WaitForSingleObject和WaitForMultipleObjects克隆。从那时起它已被采用(它在Linux / Mac上的Steam中使用)并且工作得相当好。

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