2015-09-06 45 views
1

我写了下面的代码,到目前为止在我所有的测试中,似乎我已经为我的4个线程编写了一个可用的互斥锁,但我希望得到其他人的意见我的解决方案的有效性。2+线程的自写互斥

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); 
} 
+3

这段代码是不完整的,但(或可怕的错误).. Mutex.waiting从不指着某处定义。除此之外,试图将自己的原子基元写在非原子的东西上C提供的绝对是一个坏主意,它只是很多工作(但总是可行的)来证明它可能出错的地方。 [关键就是找到一个随机的线程切换最容易出问题的点] –

+1

添加到我的评论...可靠的代码,请改用'pthread_mutex_t' ...实现可能会有所不同,但你永远也找不到依据的实现在“纯粹”C上,因为它总是需要一些原子基元。 –

+0

据我所见,在线程A已经进入之后,线程A离开它之前,什么都不能阻止线程B进入相同的互斥体。无论是锁定还是解锁,互斥锁的状态都是相同的。 – regular

回答

1

这个问题我在你代码中看到:

一个mutex背后的想法是提供相互排斥,是指当thread_a处于临界区,thread_b必须等待(如果他想也要输入)为thread_a

这个等待部分应该在enterLock函数中实现。但是你所拥有的是一个for循环,它可能在临界区完成thread_a之前结束,因此thread_b也可以进入,因此你不能互相排斥。

办法解决它:

Peterson's algorithm看一看例如或德克尔的(more complicated),他们做了什么有什么所谓busy waiting这基本上是一个while环路说: while(i can't enter) { do nothing and wait...}

+0

我很困惑,为什么你认为我的for循环可能会在thread_a完成之前结束?生病编辑自己的帖子显示在'以获取更多信息 – AndrewGrant

+0

(i = 0;我< lock-> num_processes;我++)','i'运行'num_processes'次,那么'为loop'到此为止,但是,同时'thread_a'它不会被完成,它可以执行一个沉重的任务或somtheng – ThunderWiring

+0

它运行无数次,直到一个迭代显示每个锁 - >等待[我]是错误的。您可能会错过阅读for循环 – AndrewGrant

0

enterLock()返回后,Mutex对象的状态与调用该函数之前的状态相同。因此,即使在第一个线程释放它呼叫leaveLock()之前,它也不会阻止第二个线程输入相同的Mutex对象。没有互斥性。

2

我觉得不得不在这里写一个答案,因为这个问题很好,考虑到它可以帮助其他人理解互斥的一般问题。在你的情况下,你走了很长的路要隐藏这个问题,但你无法避免它。它归结为:

01 /* pseudo-code */ 
02 if (! mutex.isLocked()) 
03  mutex.lock(); 

你总是期待线路0203之间的线程切换。因此,有两种线程发现mutex可能解锁并在此之后中断......只有稍后才能恢复并单独锁定此互斥锁。您将有两个线程同时进入关键部分。

因此,您确实需要实现可靠的互斥,这是一种原子操作,它可以测试一个条件,同时设置一个值,同时不会有任何中断的机会。

01 /* pseudo-code */ 
02 while (! test_and_lock(mutex)); 

只要这个test_and_lock功能不能被打断,你的代码是安全的。直到,C没有提供类似的东西,所以需要使用例如pthreads的实现。组装或特殊的编译器内部函数。有了,终于有了一种“标准”的方式来编写这样的原子操作,但我不能在这里举一个例子,因为我没有这方面的经验。对于一般用途,pthreads图书馆会给你你需要的东西。

编辑:当然,这仍然是简化的 - 在多处理器场景中,您需要确保偶数内存访问是互斥的。

1

你完全忽略了内存模式的话题。除非您的计算机使用顺序一致的内存模式(目前没有任何PC CPU),否则您的代码不正确,因为任何由一个线程执行的存储不一定对其他CPU立即可见。但是,这似乎是你的代码中的一个假设。

底线:使用由操作系统或运行时库这样的POSIX或者Win32 API中提供的现有的同步原语,不自作聪明和自己实现。除非你有多年的并行编程经验以及对CPU架构的深入了解,否则很可能会导致错误的实现。和调试并行programms的可能是地狱......

+0

无论机器的内存如何模型是:*编译器*可能会重新排序内存访问。 – EOF