将 Windows 手动重置事件移植到 Linux?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/178114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Porting windows manual-reset event to Linux?
提问by ephemient
Is there any easier solution in porting a windows manual-reset event to pthread, than a pthread conditional-variable + pthread mutex + a flag if event is set or unset?
是否有比 pthread 条件变量 + pthread 互斥锁 + 事件设置或取消设置的标志更简单的解决方案将 Windows 手动重置事件移植到 pthread?
回答by ephemient
Pthreads are low level constructs. No, there isn't a simpler mechanism; pthread_cond__*
is conceptually similar to an auto-reset event. Be careful, pthread_cond_wait
may have spurious wakeups, so it should never be used without some sort of external flag regardless of the situation.
Pthread 是低级结构。不,没有更简单的机制;pthread_cond__*
在概念上类似于自动重置事件。小心,pthread_cond_wait
可能会有虚假唤醒,因此无论情况如何,都不应该在没有某种外部标志的情况下使用它。
Building your own wouldn't be too hard, though.
不过,自己构建并不会太难。
#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);
}
This may not fit your usage, as you will often have a different lock that you'd want to use in place of ev->mutex
, but this is the gist of how it's typically used.
这可能不适合您的用法,因为您通常会使用不同的锁来代替ev->mutex
,但这是通常使用方式的要点。
回答by lucho
You can easy implement manual-reset events with pipes:
您可以使用管道轻松实现手动重置事件:
event is in triggered state -> there is something to read from the pipe
事件处于触发状态 -> 有一些东西可以从管道中读取
SetEvent -> write()
SetEvent -> 写()
ResetEvent -> read()
重置事件 -> 读取()
WaitForMultipleObjects -> poll() (or select()) for reading
WaitForMultipleObjects -> poll() (或 select()) 用于读取
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.
“SetEvent”操作应该写一些东西(例如任何值的 1 个字节)只是为了将管道置于非空状态,因此后续的“等待”操作,即用于读取可用数据的 poll() 不会阻塞。
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:
“ResetEvent”操作将读取写入的数据以确保管道再次为空。管道的读取端应该是非阻塞的,以便尝试重置(读取)已经重置的事件(空管道)不会阻塞 - fcntl(pipe_out, F_SETFL, O_NONBLOCK) 因为在之前可能有 1 个以上的 SetEvents ResetEvent,您应该对其进行编码,使其读取的字节数与管道中的字节数一样多:
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.
请注意,等待事件不会从管道中读取,因此“事件”将保持触发状态,直到重置操作。
回答by Angel Genchev
I prefer the pipe approach, because often one doesn't need just an event to wait for, but multiple objects e.g. WaitForMultipleObjects(...)
. And with using pipes one could easily replace the windows WaitForMultipleObjects
call with poll(...)
, select
, pselect
, and epoll
.
我更喜欢管道方法,因为通常不需要等待一个事件,而是需要多个对象,例如WaitForMultipleObjects(...)
. 并使用管道一个可以很容易地更换窗户WaitForMultipleObjects
调用与poll(...)
,select
,pselect
,和epoll
。
There was a light-weight method for process synchronization called Futex
(Fast Userspace Locking system call). There was a function futex_fd
to get one or more file descriptors for futexes. This file descriptor, together with possibly many others representing real files, devices, sockets or the like could get passed to select
, poll
, or epoll
. Unfortunately it was removedfrom the kernel. So the pipes tricks remain the only facility to do this:
有一种用于进程同步的轻量级方法,称为Futex
(快速用户空间锁定系统调用)。有一个函数futex_fd
可以为 futex 获取一个或多个文件描述符。该文件描述,可能有许多人表示真正的文件,设备,插座或类似在一起可以得到传递给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]);
回答by Vincent
No there isn't any easier solution but the following code will do the trick:
不,没有任何更简单的解决方案,但以下代码可以解决问题:
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);
}
When calling signal(), the event goes in signaled state and all waiting thread will run. Then the event will stay in signaled state and all the thread calling wait() won't wait. A call to reset() will put the event back to non-signaled state.
调用 signal() 时,事件进入信号状态,所有等待线程都将运行。然后事件将保持在有信号状态并且所有调用 wait() 的线程都不会等待。调用 reset() 会将事件恢复到无信号状态。
The signalCounter is there in case you do a quick signal/reset to wake up waiting threads.
如果您执行快速信号/重置以唤醒等待线程,则可以使用 signalCounter。
回答by Vincent
I think Windows Events are more akin to a semaphore. I.e. for auto-reset, you'd use a binary semaphore and the sem_timedwait() function.
我认为 Windows 事件更类似于信号量。即对于自动重置,您将使用二进制信号量和 sem_timedwait() 函数。
回答by Mahmoud Al-Qudsi
We were looking for a similar solution to port some heavily-multithreaded C++ code from Windows to Linux, and ended up writing an open source, MIT-licensed Win32 Events for Linux library. It should be the solution you are looking for, and has been heavily vetted for performance and resource consumption.
我们一直在寻找类似的解决方案来将一些高度多线程的 C++ 代码从 Windows 移植到 Linux,最终编写了一个开源的、MIT 许可的Win32 Events for Linux 库。它应该是您正在寻找的解决方案,并且已经过性能和资源消耗的严格。
It implements manual and auto-reset events, and both the WaitForSingleObject
and WaitForMultipleObject
functionalities.
它实现手动和自动重置事件,以及WaitForSingleObject
和WaitForMultipleObject
功能。
回答by Mahmoud Al-Qudsi
We (full disclosure: I work at NeoSmart Technologies) wrote an open source (MIT licensed) library called peventswhich implements WIN32 manual and auto-reset eventson POSIX, and includes both WaitForSingleObject and WaitForMultipleObjects clones. It's seen some adoption since then (it's used in Steam on Linux/Mac) and works fairly well.
我们(完全披露:我在 NeoSmart Technologies 工作)编写了一个名为pevents 的开源(MIT 许可)库,它在 POSIX 上实现了 WIN32 手动和自动重置事件,并且包括 WaitForSingleObject 和 WaitForMultipleObjects 克隆。从那时起它就得到了一些采用(它在 Linux/Mac 上的 Steam 中使用)并且运行良好。
Although I'd personally advise you to use POSIX multithreading and signaling paradigms when coding on POSIX machines, pevents gives you another choice if you need it.
虽然我个人建议您在 POSIX 机器上编码时使用 POSIX 多线程和信号范式,但如果您需要,pevents 为您提供了另一种选择。