我已经编写了以下代码,到目前为止,在我的所有测试中,似乎我已经为我的4个线程编写了一个有效的Mutex,但是我希望得到别人对我的有效性的看法。解。
typedef struct Mutex{
int turn;
int * waiting;
int num_processes;
} Mutex;
void enterLock(Mutex * lock, int id){
int i;
for(i = 0; i < lock->num_processes; i++){
lock->waiting[id] = 1;
if (i != id && lock->waiting[i])
i = -1;
lock->waiting[id] = 0;
}
printf("ID %d Entered\n",id);
}
void leaveLock(Mutex * lock, int id){
printf("ID %d Left\n",id);
lock->waiting[id] = 0;
}
void foo(Muted * lock, int id){
enterLock(lock,id);
// do stuff now that i have access
leaveLock(lock,id);
}
答案 0 :(得分:2)
我觉得有必要在这里写一个答案,因为问题很好,考虑到它可以帮助其他人理解互斥的一般问题。在您的情况下,您可以很长时间地隐藏此问题,但您无法避免此问题。归结为:
01 /* pseudo-code */
02 if (! mutex.isLocked())
03 mutex.lock();
您始终需要在行02
和03
之间切换线程。因此,有一种情况可能会导致两个线程发现mutex
未锁定并在此之后被中断...仅在稍后恢复并单独锁定此互斥锁。您将有两个线程同时进入临界区。
实现可靠互斥所必需的是一个原子操作,它可以测试一个条件,同时设置一个值而不会有任何机会被同时中断。
01 /* pseudo-code */
02 while (! test_and_lock(mutex));
只要此test_and_lock
函数无法中断,您的实现就是安全的。在c11之前,C没有提供这样的内容,因此pthreads
的实现需要使用,例如汇编或特殊编译器内在函数。使用c11,最终会有一个&#34;标准&#34;写这样的原子操作的方法,但我不能在这里给出一个例子,因为我没有经验这样做。对于一般用途,pthreads
库将为您提供所需的信息。
编辑当然,这仍然是简化的 - 在多处理器方案中,您需要确保即使是内存访问也是互斥的。
答案 1 :(得分:1)
我在您的代码中看到的问题:
mutex
背后的想法是提供互斥,意味着当thread_a
位于关键部分时,thread_b
必须等待(如果他也想要进入){ {1}}。
此等待部分应在thread_a
函数中实现。但是你所拥有的是enterLock
循环,它可能会在for
从关键部分完成之前结束,因此thread_a
也可以进入,因此你不能互相排斥。
解决问题的方法:
以Peterson's algorithm或Dekker(more complicated)为例,他们所做的是thread_b
所谓的busy waiting
基本上是{{ 1}}循环说:
while
答案 2 :(得分:1)
你完全无视内存模型的话题。除非您使用的是具有顺序一致内存模型的计算机(当前没有PC CPU),否则您的代码不正确,因为一个线程执行的任何存储不一定立即对其他CPU可见。但是,这似乎是您代码中的假设。
底线:使用操作系统提供的现有同步原语或POSIX或Win32 API等运行时库,不要试图变得聪明并自己实现。除非您在并行编程方面有多年的经验以及对CPU架构的深入了解,否则您最终可能会遇到错误的实现。调试并行程序可能是地狱......
答案 3 :(得分:0)
在enterLock()
返回后,Mutex对象的状态与调用函数之前的状态相同。因此,即使在第一个线程释放它调用leaveLock()
之前,它也不会阻止第二个线程进入相同的Mutex对象。没有相互排斥。