首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >pthread_mutex_unlock不是原子的

pthread_mutex_unlock不是原子的
EN

Stack Overflow用户
提问于 2010-12-08 20:18:31
回答 4查看 1.5K关注 0票数 1

我有以下源代码(改编自我的原始代码):

代码语言:javascript
复制
#include "stdafx.h"
#include <stdlib.h> 
#include <stdio.h> 

#include "pthread.h"

#define MAX_ENTRY_COUNT 4  

int  entries = 0;  
bool start = false;

bool send_active = false;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
pthread_cond_t condNotEmpty = PTHREAD_COND_INITIALIZER;  
pthread_cond_t condNotFull = PTHREAD_COND_INITIALIZER;  

void send()
{ 
    for (;;) {
        if (!start)
            continue;
        start = false;

        for(int i = 0; i < 11; ++i) { 
            send_active = true;

            pthread_mutex_lock(&mutex); 
            while(entries == MAX_ENTRY_COUNT) 
                pthread_cond_wait(&condNotFull, &mutex);      
            entries++; 
            pthread_cond_broadcast(&condNotEmpty); 
            pthread_mutex_unlock(&mutex);

            send_active = false;
        }
    }
} 

void receive(){ 
    for(int i = 0; i < 11; ++i){ 
        pthread_mutex_lock(&mutex);  
        while(entries == 0) 
            pthread_cond_wait(&condNotEmpty, &mutex); 
        entries--;  
        pthread_cond_broadcast(&condNotFull);  
        pthread_mutex_unlock(&mutex);
    } 

    if (send_active)
        printf("x");
} 

int _tmain(int argc, _TCHAR* argv[])
{
    pthread_t s; 

    pthread_create(&s, NULL, (void *(*)(void*))send, NULL);  

    for (;;) {
        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&condNotEmpty, NULL);
        pthread_cond_init(&condNotFull, NULL);

        start = true;

        receive();

        pthread_mutex_destroy(&mutex);
        mutex = NULL;
        pthread_cond_destroy(&condNotEmpty);
        pthread_cond_destroy(&condNotFull);
        condNotEmpty = NULL;
        condNotFull = NULL;

        printf(".");
    }

    return 0;
}

问题如下:在接收方法继续之前,发送函数中的最后一次解锁并未完成。在我的原始代码中,互斥锁位于一个对象中,这些对象在完成工作后被删除。如果send方法还没有完成最后一次解锁,那么互斥锁是无效的,并且我的程序会导致解锁失败。

通过运行程序可以很容易地重现这种行为:每次显示"x“时,接收方法已接近完成,而发送方法在解锁调用中”挂起“。

我已经用VS2008和VS2010编译过了--两个结果是一样的。

pthread_mutex_unlock不是原子的,这就解决了问题。我该如何解决这个问题?欢迎任何评论……

诚挚的问候

迈克尔

EN

回答 4

Stack Overflow用户

发布于 2010-12-08 22:09:54

您的printf("x")是一个典型的竞态条件示例。

在pthread_mutex_unlock()之后,OS可以自由地在任何时间内不调度这个线程:滴答、秒或天。你不能假设send_active最终会被“证伪”。

票数 1
EN

Stack Overflow用户

发布于 2010-12-08 23:01:02

根据定义,pthread_mutex_unlock()必须在互斥锁返回之前释放它。一旦互斥锁被释放,另一个争用互斥锁的线程就会被调度。请注意,即使pthread_mutex_unlock()可以安排在互斥锁返回之前不释放它(我认为您说的是原子的),仍然会有一个与您现在看到的东西等价的竞争条件(我不清楚您看到的是什么竞争,因为有评论表明您对访问send_active来控制printf()调用的竞争条件并不真正感兴趣)。

在这种情况下,另一个线程可以被调度到pthread_mutex_unlock()的“行间”,以及调用它的函数中的以下语句/表达式-你会有相同的竞争条件。

票数 1
EN

Stack Overflow用户

发布于 2010-12-09 14:31:33

这里有一些关于可能发生的事情的猜测。对这一分析有几个警告:

  • 这是基于这样的假设,即您使用的是http://sourceware.org/pthreads-win32/
  • this的Win32 pthreads包,只是基于对该站点上的pthreads源文件以及此处问题和评论中的信息进行了相当简短的检查-我还没有机会实际尝试运行/调试任何代码。

当调用pthread_mutex_unlock()时,它会递减锁计数,如果锁计数降为零,则会在关联的事件对象上调用Win32 SetEvent()应用编程接口,以便解除对等待互斥锁的任何线程的阻塞。非常标准的东西。

这就是投机的原因。假设已经调用了SetEvent()来解除阻塞等待互斥锁的线程,并且它向与它被提供的句柄相关联的事件发出信号(这是它应该做的)。但是,在SetEvent()函数执行其他操作之前,另一个线程开始运行并关闭调用该特定SetEvent()的事件对象句柄(通过调用pthread_mutex_destroy())。

现在,正在进行的SetEvent()调用有一个不再有效的句柄值。我想不出在通知事件之后SetEvent()会对该句柄做什么特殊原因,但它可能会这样做(我还可以想象有人会提出一个合理的论点,即SetEvent()应该能够期望事件对象句柄在调用期间保持有效)。

如果这就是发生在你身上的事情(这是一个很大的假设),我不确定是否有简单的解决办法。我认为pthread库必须进行更改,以便在调用SetEvent()之前复制事件句柄,然后在SetEvent()调用返回时关闭该副本。这样,即使“main”句柄被另一个线程关闭,句柄也将保持有效。我猜它必须在很多地方做到这一点。这可以通过将受影响的Win32应用程序接口调用替换为对执行“复制处理/调用应用编程接口/关闭复制”序列的包装函数的调用来实现。

对于您来说,尝试对pthread_mutex_unlock()中的SetEvent()调用进行此更改并不是不合理的,并查看它是否解决(或至少改进)您的特定问题。如果是这样的话,你可能想要联系这个库的维护者,看看是否可以以某种方式安排一个更全面的修复(做好准备--你可能会被要求做大部分工作)。

出于好奇,在调试pthread_mutex_unlock()/SetEvent()中挂起的线程的状态时,您有关于到底发生了什么的信息吗?SetEvent()在等什么?(与Visual Studio调试器相比,Windows调试工具包中的cdb调试器可能能够提供更多有关这方面的信息)。

此外,请注意pthread_mutex_destroy()源代码中的以下注释,它似乎与您的特定问题相关(但不同):

代码语言:javascript
复制
/*
 * FIXME!!!
 * The mutex isn't held by another thread but we could still
 * be too late invalidating the mutex below since another thread
 * may already have entered mutex_lock and the check for a valid
 * *mutex != NULL.
 *
 * Note that this would be an unusual situation because it is not
 * common that mutexes are destroyed while they are still in
 * use by other threads.
*/
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4387327

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档