1

在 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 );

我在这里没有看到任何错误,并希望得到一些关于可能出错的提示,特别是因为它在一些随机时间工作。

4

1 回答 1

1

一旦我写了 95% 的问题,这个错误就让我眼前一亮……我仍然决定把它和修复一起放在这里,以防其他人偶然发现类似的东西。创建互斥锁和等待条件的部分如下所示:

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
}

如果您仔细看,您会发现 ftruncate() 截断为 eventData 指针的大小,而不是 struct EventData 的大小。所以,这里需要的一个字符修复是:

if ( ftruncate(fd, sizeof (EventData )) < 0 ) {
    // failed to truncate file
}

确实是愚蠢的错误。

于 2012-11-14T11:14:24.947 回答