在具有4个线程的程序上调用pthread_cond_signal时,同一线程将获得互斥体

时间:2018-10-04 23:58:17

标签: c multithreading pthreads mutex race-condition

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t node[4];
pthread_mutex_t token;
pthread_cond_t cond;
int id=0;

void *func(int n)
   {
        int count = 0;
        while (count < 10){
                 pthread_mutex_lock(&token);
                while (id != n){
                        printf("Whoops not my turn, id=%d\n",n);
                        pthread_cond_wait(&cond, &token);}
                //if (id == n){
                        count += 1;
                        printf ("My turn! id= %d\n",n);
                        printf("count %d\n", count);
                        if (id == 3){
                                id = 0;}
                        else{
                                id += 1;}
                        //}else{
                        //      printf("Not my turn! id=%d\n",n);}
                        //      pthread_mutex_unlock(&token);
                        //      sleep(2);}
                        pthread_mutex_unlock(&token);
                        pthread_cond_signal(&cond);}
                        printf ("ID=%d has finished\n",n);

        return(NULL);
   }

int main()
   {
   int i;
   pthread_mutex_init(&token,NULL);
        pthread_cond_init(&cond,NULL);
   for(i=0;i<4;i++)
      pthread_create(&node[i],NULL,(void *)func,(void *)i);

   for(i=0;i<4;i++)
      pthread_join(node[i],NULL);

   pthread_mutex_destroy(&token);

   return 0;
   }

这是我的代码,它是一个使用线程的C程序。我认为这里不需要知道它的目的,但是我将举例说明我遇到的问题。

id为1(在func中由n定义)的说线程获得互斥量,并返回“我的转弯!id = 1”。当调用mutex_unlock和cond_signal时,下一个获取互斥锁的线程实际上将再次是ID为1的线程,并且它将显示“ Whoops not my turn,id = 1”。然后只有id为2的线程才能获取互斥锁,并打印“ My turn!id = 2”,但是当id为2的线程之后便可以获取互斥锁。这是我的程序输出:

Whoops not my turn, id=1
Whoops not my turn, id=2
Whoops not my turn, id=3
My turn! id= 0
count 1
Whoops not my turn, id=0
My turn! id= 1
count 1
Whoops not my turn, id=1
My turn! id= 2
count 1
Whoops not my turn, id=2
My turn! id= 3
count 1
Whoops not my turn, id=3
My turn! id= 0
count 2
Whoops not my turn, id=0
My turn! id= 1
count 2
Whoops not my turn, id=1
My turn! id= 2
count 2
Whoops not my turn, id=2
My turn! id= 3
count 2
Whoops not my turn, id=3
My turn! id= 0
count 3
Whoops not my turn, id=0
My turn! id= 1
count 3
Whoops not my turn, id=1
My turn! id= 2
count 3
Whoops not my turn, id=2
My turn! id= 3
count 3
Whoops not my turn, id=3
My turn! id= 0
count 4
Whoops not my turn, id=0
My turn! id= 1
count 4
Whoops not my turn, id=1
My turn! id= 2
count 4
Whoops not my turn, id=2
My turn! id= 3
count 4
Whoops not my turn, id=3
My turn! id= 0
count 5
Whoops not my turn, id=0
My turn! id= 1
count 5
Whoops not my turn, id=1
My turn! id= 2
count 5
Whoops not my turn, id=2
My turn! id= 3
count 5
Whoops not my turn, id=3
My turn! id= 0
count 6
Whoops not my turn, id=0
My turn! id= 1
count 6
Whoops not my turn, id=1
My turn! id= 2
count 6
Whoops not my turn, id=2
My turn! id= 3
count 6
Whoops not my turn, id=3
My turn! id= 0
count 7
Whoops not my turn, id=0
My turn! id= 1
count 7
Whoops not my turn, id=1
My turn! id= 2
count 7
Whoops not my turn, id=2
My turn! id= 3
count 7
Whoops not my turn, id=3
My turn! id= 0
count 8
Whoops not my turn, id=0
My turn! id= 1
count 8
Whoops not my turn, id=1
My turn! id= 2
count 8
Whoops not my turn, id=2
My turn! id= 3
count 8
Whoops not my turn, id=3
My turn! id= 0
count 9
Whoops not my turn, id=0
My turn! id= 1
count 9
Whoops not my turn, id=1
My turn! id= 2
count 9
Whoops not my turn, id=2
My turn! id= 3
count 9
Whoops not my turn, id=3
My turn! id= 0
count 10
ID=0 has finished
My turn! id= 1
count 10
ID=1 has finished
My turn! id= 2
count 10
ID=2 has finished
My turn! id= 3
count 10
ID=3 has finished

如您所见,每次成功后,线程将打印“我的转弯!”,此后它将得到互斥锁,并导致“不要转我的转弯!”。我不明白为什么会在调用pthread_cond_signal时发生这种情况,在当前线程可以重新获取互斥锁之前,pthread_cond_signal应该向另一个线程发出信号。请帮助我找到此解决方案,因为我认为我缺少一些重要的东西。如果缺少我的解释,请随时向我询问更多信息。非常感谢您的宝贵时间!

2 个答案:

答案 0 :(得分:3)

对于Linux以及一般情况下的posix系统,不能保证互斥锁请求得到服务的顺序。互斥锁解锁后,操作系统不会强制进行上下文切换,因此当前正在运行的线程将继续其循环并再次锁定互斥锁。我不知道是否可以通过pthread接口更改线程优先级,但是如果可以的话,您可以更改线程“(n)%4”的优先级,然后在线程“(n)%4”运行时,它将其优先级恢复为正常,并将线程“(n + 1)%4”设置为更高的优先级。

对于Windows使用其本机互斥锁的情况,显然在队列或等效队列中跟踪锁定请求的顺序,因此,当当前正在运行的线程循环回到锁定请求时,Windows将切换到线程中的第一个线程互斥锁的锁定请求队列。我不知道这是否已记录,但我已经确认它可以这种方式工作。我不知道Windows中的pthreads互斥锁是否可以这种方式工作。

一个可能的替代方案是每个线程一个互斥体,但是我不知道这是否会导致任何问题,具体取决于操作系统。每个线程使用一个信号灯应该有效

请注意,如果使用条件变量,则可能会导致虚假唤醒,并且需要处理这种情况。

https://en.wikipedia.org/wiki/Spurious_wakeup

但是,如果使用互斥,信号量等本地Windows同步类型,则不会发生虚假唤醒。

对于某些操作系统(例如Linux),这些问题已足够解决一些高端多处理/多线程应用程序安装内核级驱动程序的问题,以便实现内核时间自旋锁来避免这些问题。 / p>

https://en.wikipedia.org/wiki/Spinlock

答案 1 :(得分:0)

@rcgldr已经很好地说明了所涉及的时间。如果您想增加给另一个线程一个机会的机会,请尝试添加一个对pthread_yield的调用,这应该使调度程序有机会选择另一个线程,尽管这也不是保证。 / p>