在 Linux 上使用来自 pthread 的等待条件和互斥锁在进程之间进行同步时,我遇到了一个奇怪的问题。请注意,这不仅仅在一个进程中的线程之间。
我的用例是有一个生产者创建资源(在我的例子中是图像),将它们保存到共享内存区域,更新有关资源的一些信息,然后向等待的消费者发出信号。共享内存和元数据部分工作正常,所以我会忽略它,问题是信号不能可靠地工作。用例很简单,消费者错过一两张图片并不重要,如果消费者还没有时间阅读,生产者基本上只是覆盖一张旧图片。所以等待条件只需要处理唤醒消费者,我不需要任何资源计数或其他数据。
生产者和消费者都有这样的结构:
struct EventData {
pthread_mutex_t mutexHandle;
pthread_cond_t conditionHandle;
};
消费者进程中的一个线程坐下来等待某事发生:
pthread_mutex_lock( &eventData->mutexHandle );
pthread_cond_wait( &eventData->conditionHandle, &eventData->mutexHandle );
thread_mutex_unlock( &eventData->mutexHandle );
生产过程在创建图像、将其保存到共享内存并准备好让消费者抓取图像时执行此操作:
pthread_mutex_lock( &eventData->mutexHandle );
pthread_cond_signal( &eventData->conditionHandle );
// also tried:
//pthread_cond_broadcast( &eventData->conditionHandle );
pthread_mutex_unlock( &eventData->mutexHandle );
这对我来说看起来很不错,并且在某种程度上有效。生产者可以毫无问题地向消费者发出大约 100-1000 次信号,消费者醒来,抓取图像并显示它,结果是我可以看到移动的视频。在某些时候,通常大约几百帧,消费者将在 pthread_cond_wait() 中冻结并且永远不会返回。生产者仍然愉快地创建图像,调用 pthread_cond_signal() 并继续没有问题。消费者没有完全冻结,只有执行 pthread_cond_wait() 的线程,应用程序的其余部分运行没有问题。
因此,当信号从一个线程移动到另一个进程中的另一个线程时,某些东西会导致信号丢失。消费者冻结之前通常需要 5-20 秒,并且唤醒工作的次数也在 100 到 1000 之间变化(基于目前看到的值)。
由于默认情况下在进程之间共享互斥锁和等待条件并非易事,因此我使用此设置来创建原语:
EventData * eventData;
int fd = open( tmpnam(NULL), O_RDWR | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
// failed to open file for event
}
if ( ftruncate(fd, sizeof (eventData )) < 0 ) {
// failed to truncate file
}
// setup attributes to allow sharing between processes
pthread_condattr_init( &conditionAttribute );
pthread_condattr_setpshared( &conditionAttribute, PTHREAD_PROCESS_SHARED );
pthread_mutexattr_init( &mutexAttribute );
pthread_mutexattr_setpshared( &mutexAttribute, PTHREAD_PROCESS_SHARED );
// map memory for the event struct
eventData = (EventData *) mmap(NULL, sizeof(EventData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
// finally initialize the memory
pthread_mutex_init( &eventData->mutexHandle, &mutexAttribute );
pthread_cond_init( &eventData->conditionHandle, &conditionAttribute );
以上是由创建互斥锁和等待条件的一方完成的。文件的名称,即 tmpnam(NULL) 实际上已保存并传递给其他进程以进行打开:
int fd = open( nameOfEventFile, O_RDWR, 0666 );
if (fd < 0) {
// failed to open file for event
}
eventData = (EventData *) mmap( NULL, sizeof(EventData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
close( fd );
我在这里没有看到任何错误,并希望得到一些关于可能出错的提示,特别是因为它在一些随机时间工作。