C 在Linux上等待多个条件变量而不进行不必要的睡眠?
我正在编写一个对延迟敏感的应用程序,实际上它希望同时等待多个条件变量。我以前读过几种在Linux上获得此功能的方法(显然这是Windows上内置的),但它们似乎都不适合我的应用程序。我知道的方法有:C 在Linux上等待多个条件变量而不进行不必要的睡眠?,c,multithreading,pthreads,conditional-statements,scheduling,C,Multithreading,Pthreads,Conditional Statements,Scheduling,我正在编写一个对延迟敏感的应用程序,实际上它希望同时等待多个条件变量。我以前读过几种在Linux上获得此功能的方法(显然这是Windows上内置的),但它们似乎都不适合我的应用程序。我知道的方法有: 让一个线程等待您要等待的每个条件变量,当唤醒它时,它将向您等待的单个条件变量发出信号 通过定时等待循环多个条件变量 将虚拟字节写入文件或管道,并轮询这些文件或管道 #1和2不合适,因为它们会导致不必要的睡眠。对于#1,您必须等待虚拟线程唤醒,然后向真实线程发送信号,然后让真实线程唤醒,而不是让真实线
有更好的方法吗?想知道适用于Solaris的答案吗。如果您谈论的是POSIX线程,我建议您使用单个条件变量和事件标志数或类似的东西。其思想是使用对等condvar互斥来保护事件通知。无论如何,您需要在cond_wait()退出后检查事件。这是我的老代码,可以从我的培训中说明这一点(是的,我检查了它是否运行,但请注意,它是在一段时间前准备好的,对于新手来说很匆忙)
#包括
#包括
#包括
静态pthread_cond_t var;
静态pthread_mutex_t mtx;
未签名事件\u标志=0;
#定义标志\u事件\u 1
#定义标志\u事件\u 2
无效信号_1()
{
pthread_mutex_lock(&mtx);
事件|标志|=标志(事件)1;
pthread_cond_信号(&var);
pthread_mutex_unlock(&mtx);
}
无效信号_2()
{
pthread_mutex_lock(&mtx);
事件|标志|=标志(事件)2;
pthread_cond_信号(&var);
pthread_mutex_unlock(&mtx);
}
void*处理程序(void*)
{
//只有当我们等待或处理接收到的事件时,互斥锁才会解锁。
pthread_mutex_lock(&mtx);
//这里应该是真实代码中的竞争条件预防。
而(1)
{
如果(事件标志)
{
未签名副本=事件标志;
//我们在处理接收到的事件时解锁互斥锁。
pthread_mutex_unlock(&mtx);
if(复制并标记事件1)
{
printf(“事件1\n”);
复制^=标志事件1;
}
if(复制并标记事件2)
{
printf(“事件2\n”);
复制^=标志事件2;
//让事件2成为“退出”信号。
//在这种情况下,为了保持一致性,我们使用锁定互斥来中断。
pthread_mutex_lock(&mtx);
打破
}
//注意,我们应该在迭代结束时锁定互斥锁。
pthread_mutex_lock(&mtx);
}
其他的
{
//互斥锁已锁定。在我们等待时,它已解锁。
pthread_cond_wait(&var,&mtx);
//互斥锁已锁定。
}
}
//…我们快死了。
pthread_mutex_unlock(&mtx);
}
int main()
{
pthread_mutex_init(&mtx,NULL);
pthread_cond_init(&var,NULL);
pthread_t id;
pthread_创建(&id,NULL,handler,NULL);
睡眠(1);
信号_1();
睡眠(1);
信号_1();
睡眠(1);
信号_2();
睡眠(1);
pthread_join(id,NULL);
返回0;
}
对于等待多个条件变量,如果您感兴趣,可以将Solaris的一个实现移植到Linux:您的#3选项(将虚拟字节写入文件或管道,并对其进行轮询)在Linux上有更好的选择:
使用eventfd
可以使用内核内无符号64位计数器,而不是有限大小的缓冲区(如管道中)或无限增长的缓冲区(如文件中)。8字节的写入
向计数器添加一个数字;8字节的read
要么将计数器归零并返回其以前的值(无EFD\u信号量
),要么将计数器减量1并返回1(有EFD\u信号量
)。当计数器非零时,文件描述符被认为是轮询函数可读的(select
,poll
,epoll
)
即使计数器接近64位限制,如果使文件描述符非阻塞,写入
操作也将失败,并显示EAGAIN
。计数器为零时,read
也会发生同样的情况。如果您希望在POSIX条件变量同步模型下获得最大的灵活性,则必须避免编写仅通过公开条件变量向其用户传递事件的模块。(然后您基本上重新发明了一个信号量。)
活动模块的设计应确保其接口通过注册的函数提供事件的回调通知:如果需要,还应确保可以注册多个回调
多个模块的客户端向每个模块注册回调。这些都可以路由到一个公共位置,在那里它们锁定相同的互斥锁、更改某些状态、解锁并命中相同的条件变量
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
static pthread_cond_t var;
static pthread_mutex_t mtx;
unsigned event_flags = 0;
#define FLAG_EVENT_1 1
#define FLAG_EVENT_2 2
void signal_1()
{
pthread_mutex_lock(&mtx);
event_flags |= FLAG_EVENT_1;
pthread_cond_signal(&var);
pthread_mutex_unlock(&mtx);
}
void signal_2()
{
pthread_mutex_lock(&mtx);
event_flags |= FLAG_EVENT_2;
pthread_cond_signal(&var);
pthread_mutex_unlock(&mtx);
}
void* handler(void*)
{
// Mutex is unlocked only when we wait or process received events.
pthread_mutex_lock(&mtx);
// Here should be race-condition prevention in real code.
while(1)
{
if (event_flags)
{
unsigned copy = event_flags;
// We unlock mutex while we are processing received events.
pthread_mutex_unlock(&mtx);
if (copy & FLAG_EVENT_1)
{
printf("EVENT 1\n");
copy ^= FLAG_EVENT_1;
}
if (copy & FLAG_EVENT_2)
{
printf("EVENT 2\n");
copy ^= FLAG_EVENT_2;
// And let EVENT 2 to be 'quit' signal.
// In this case for consistency we break with locked mutex.
pthread_mutex_lock(&mtx);
break;
}
// Note we should have mutex locked at the iteration end.
pthread_mutex_lock(&mtx);
}
else
{
// Mutex is locked. It is unlocked while we are waiting.
pthread_cond_wait(&var, &mtx);
// Mutex is locked.
}
}
// ... as we are dying.
pthread_mutex_unlock(&mtx);
}
int main()
{
pthread_mutex_init(&mtx, NULL);
pthread_cond_init(&var, NULL);
pthread_t id;
pthread_create(&id, NULL, handler, NULL);
sleep(1);
signal_1();
sleep(1);
signal_1();
sleep(1);
signal_2();
sleep(1);
pthread_join(id, NULL);
return 0;
}