将窗口手动重置事件移植到pthread是否有更简单的解决方案, 如果事件设置或未设置,那么pthread条件变量+ pthread互斥锁+标志?
答案 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(...)
。使用管道时,可以使用WaitForMultipleObjects
,poll(...)
,select
和pselect
轻松替换Windows epoll
来电。
有一种称为Futex
(快速用户空间锁定系统调用)的轻量级进程同步方法。有一个函数futex_fd
来获取futexes的一个或多个文件描述符。该文件描述符以及可能代表真实文件,设备,套接字等的许多其他文件描述符可以传递给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]);
答案 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。它应该是您正在寻找的解决方案,并且已经过严格的性能和资源消耗审查。
它实现了手动和自动重置事件,以及WaitForSingleObject
和WaitForMultipleObject
功能。
答案 6 :(得分:0)
我们(完全披露:我在NeoSmart Technologies工作)编写了一个名为pevents的开源(MIT许可)库,其implements WIN32 manual and auto-reset events位于POSIX上,并包含WaitForSingleObject和WaitForMultipleObjects克隆。从那时起它已被采用(它在Linux / Mac上的Steam中使用)并且工作得相当好。
虽然我个人建议您在POSIX机器上编码时使用POSIX多线程和信令范例,但如果您需要,pevents会为您提供另一种选择。