将Windows手动重置事件移植到pthread,除了使用pthread条件变量+ pthread互斥锁+标志指示事件已设置或未设置之外,是否还有更简单的解决方案?
将Windows手动重置事件移植到pthread,除了使用pthread条件变量+ pthread互斥锁+标志指示事件已设置或未设置之外,是否还有更简单的解决方案?
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
的位置使用它,但这通常是如何使用的要点。
char buf[256]; // 256 is arbitrary
while( read(pipe_out, buf, sizeof(buf)) == sizeof(buf));
WaitForMultipleObjects(...)
。使用管道可以轻松地将Windows的WaitForMultipleObjects
调用替换为poll(...)
、select
、pselect
和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]);
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是为了在快速发出信号/重置以唤醒等待线程时使用。
WaitForSingleObject
和WaitForMultipleObject
功能。我认为Windows事件更类似于信号量。即,对于自动重置,您可以使用二进制信号量和sem_timedwait()函数。
我们(完整披露:我在NeoSmart Technologies工作)编写了一个开源(MIT许可证)库,名为pevents,它在POSIX上实现WIN32手动和自动重置事件,包括WaitForSingleObject和WaitForMultipleObjects克隆。自那以后它已经得到了一些采用(在Linux / Mac上的Steam中使用),并且工作得相当不错。
虽然我个人建议您在POSIX机器上编码时使用POSIX多线程和信号范例,但如果需要,pevents会给您另一个选择。