pthread_mutex_unlock非原子
我有以下源代码(改编自我的原始代码):pthread_mutex_unlock非原子,c,multithreading,winapi,pthreads,C,Multithreading,Winapi,Pthreads,我有以下源代码(改编自我的原始代码): #包括“stdafx.h” #包括 #包括 #包括“pthread.h” #定义最大输入计数4 整数项=0; bool start=false; bool send_active=false; pthread\u mutex\u t mutex=pthread\u mutex\u初始值设定项; pthread_cond_t condNotEmpty=pthread_cond_初始值设定项; pthread_cond_t condNotFull=pthrea
#包括“stdafx.h”
#包括
#包括
#包括“pthread.h”
#定义最大输入计数4
整数项=0;
bool start=false;
bool send_active=false;
pthread\u mutex\u t mutex=pthread\u mutex\u初始值设定项;
pthread_cond_t condNotEmpty=pthread_cond_初始值设定项;
pthread_cond_t condNotFull=pthread_cond_初始值设定项;
作废发送()
{
对于(;;){
如果(!开始)
继续;
开始=错误;
对于(int i=0;i<11;++i){
send_active=true;
pthread_mutex_lock(&mutex);
while(条目数==最大条目数)
pthread_cond_wait(&condNotFull,&mutex);
条目++;
pthread_cond_broadcast(&condNotEmpty);
pthread_mutex_unlock(&mutex);
send_active=false;
}
}
}
void receive(){
对于(int i=0;i<11;++i){
pthread_mutex_lock(&mutex);
while(条目==0)
pthread_cond_wait(&condNotEmpty,&mutex);
条目--;
pthread_cond_广播(&condNotFull);
pthread_mutex_unlock(&mutex);
}
如果(发送_活动)
printf(“x”);
}
int _tmain(int argc,_TCHAR*argv[]
{
pthread_t s;
pthread_创建(&s,NULL,(void*(*)(void*))发送,NULL);
对于(;;){
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&condNotEmpty,NULL);
pthread_cond_init(&condNotFull,NULL);
开始=真;
接收();
pthread_mutex_destroy(&mutex);
互斥=空;
pthread_cond_destroy(&condNotEmpty);
pthread_cond_destroy(&condNotFull);
condNotEmpty=NULL;
condNotFull=NULL;
printf(“.”);
}
返回0;
}
问题如下:有时,在接收方法继续之前,发送函数中的最后一次解锁没有完成。在我的原始代码中,互斥体位于一个对象中,该对象在完成该工作后被删除。如果发送方法尚未完成最后一次解锁,则互斥锁无效,并且我的程序导致解锁失败
通过运行该程序可以很容易地再现该行为:每次显示“x”时,接收方法几乎完成,发送方法在解锁调用中“挂起”
我使用VS2008和VS2010进行了编译-两个结果都是相同的
pthread_mutex_unlock不是原子的,这将解决问题。我如何解决这个问题?欢迎任何意见
致意
迈克尔你的printf(“x”)是一个教科书式的比赛条件示例
在pthread_mutex_unlock()之后,OS可以自由地在任何时间内不安排此线程:滴答、秒或天。您不能假设send\u active会在时间上被“伪造”。pthread\u mutex\u unlock()必须根据定义在返回前释放该互斥体。释放互斥体的那一刻,就可以安排另一个争夺互斥体的线程。请注意,即使pthread\u mutex\u unlock()
可以安排在互斥对象返回后才释放互斥对象(我认为您的意思是它是原子的),仍然会有一个与您现在看到的内容等效的争用条件(我不清楚您看到的是什么种族,因为一条评论表明您对访问send\u active
以控制printf()
调用的种族条件并不感兴趣)
在这种情况下,可以在调用它的函数中的
pthread\u mutex\u unlock()
和以下语句/表达式的“行之间”调度另一个线程-您将具有相同的竞争条件。下面是一些关于可能发生的情况的推测。此分析中有几个注意事项:
- 这是基于这样一种假设,即您使用的是来自的Win32 pthreads包
- 这只是基于对该站点上pthreads源代码的非常简短的检查以及这里的问题和评论中的信息——我还没有机会实际运行/调试任何代码
pthread\u mutex\u unlock()
时,它会减少锁计数,如果锁计数降至零,则会在关联的事件对象上调用Win32SetEvent()
API,以允许任何等待互斥锁的线程解锁。非常标准的东西
这就是推测的原因。假设调用了SetEvent()
来解除对等待互斥锁的线程的阻止,它会发出与给定句柄相关联的事件的信号(应该如此)。但是,在SetEvent()之前
函数执行任何其他操作时,另一个线程开始运行并关闭调用该特定SetEvent()
的事件对象句柄(通过调用pthread\u mutex\u destroy()
)
现在,SetEvent()
正在进行的调用有一个句柄值不再有效。我想不出有什么特别的原因,SetEvent()
在发出事件信号后会对该句柄做任何事情,但可能会这样做(我还可以想象有人提出一个合理的论点,SetEvent()
应该能够预期事件对象句柄在API调用期间保持有效)
如果这就是发生在您身上的情况(这是一个很大的如果),我不确定是否有一个简单的修复方法。我认为pthreads库必须进行更改,以便在调用SetEvent()
之前复制事件句柄,然后在调用SetEvent()时关闭该副本
返回调用。这样,即使“主”句柄被另一个线程关闭,句柄仍将保持有效。我猜它必须在许多地方执行此操作。这可以通过将受影响的Win32 API调用替换为对执行“复制句柄/call API/close duplicate”se的包装函数的调用来实现
#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;
}
/*
* 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.
*/