user-0 提问: 猴子过峡谷问题，操作系统PV操作与信号量

回复:
user: user-1
content: 我看过代码了，这里线程的部分应该是没有问题的，问题出在传入的参数&i。 你描述的问题进出现在等待的线程中。因为线程的特殊性，当等待的线程取得过桥的锁之后，可以顺利过桥，但是在执行 printf("第%d只猴子向东======>>      \n",i);时，由于pthread_create(&wwid[i],NULL,westWard,&i);传入的是i的地址，而此时已经经过了较长时间的等待（也许很短），i已经发生改变。如果此时正好有一堆单方向（东向西，或者西向东）等待的猴子获取了过桥的权限，并执行过桥（打印printf），而此时，i在for循环中的值正好为k，那么这一堆过桥的猴子的ID就都是一样的了，就都是k。  解决的方案，额，比较土了，直接写10遍创建线程的语句，如下： #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h>  #define N_MONKEY 10 pthread_t ewid[N_MONKEY],wwid[N_MONKEY]; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;    //东西互斥 pthread_mutex_t mtEastWard=PTHREAD_MUTEX_INITIALIZER;    //向东计数互斥 pthread_mutex_t mtWestWard=PTHREAD_MUTEX_INITIALIZER;    //向西计数互斥  volatile int cnt = 0; int id[10] = {0,1,2,3,4,5,6,7,8,9};   void *eastWard(void *ptr) {     pthread_mutex_lock(&mtEastWard);     if(cnt==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);         cnt++;         pthread_mutex_unlock(&mtEastWard);          //crossEastWard();//猴子通过         printf("第%ld只猴子向东======>>      \n",*((int*)ptr));          pthread_mutex_lock(&mtEastWard);         cnt--;     }     if(cnt==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);      pthread_mutex_unlock(&mtEastWard);     sleep(1);      pthread_exit((void *) 0); }  void *westWard(void *ptr) {     pthread_mutex_lock(&mtWestWard);      if(cnt==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);          cnt++;          pthread_mutex_unlock(&mtWestWard);          //crossWestWard();//猴子通过         printf("      <<======第%ld只猴子向西\n",*((int*)ptr));          pthread_mutex_lock(&mtWestWard);         cnt--;     }     if(cnt==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);      pthread_mutex_unlock(&mtWestWard);     sleep(1);      pthread_exit((void *) 0); }   int main() {     int i = 0;     while(1)     {         pthread_create(&ewid[0],NULL,eastWard,&id[0]);         pthread_create(&ewid[1],NULL,eastWard,&id[1]);         pthread_create(&ewid[2],NULL,eastWard,&id[2]);         pthread_create(&ewid[3],NULL,eastWard,&id[3]);         pthread_create(&ewid[4],NULL,eastWard,&id[4]);         pthread_create(&ewid[5],NULL,eastWard,&id[5]);         pthread_create(&ewid[6],NULL,eastWard,&id[6]);         pthread_create(&ewid[7],NULL,eastWard,&id[7]);         pthread_create(&ewid[8],NULL,eastWard,&id[8]);         pthread_create(&ewid[9],NULL,eastWard,&id[9]);          pthread_create(&wwid[0],NULL,westWard,&id[0]);         pthread_create(&wwid[1],NULL,westWard,&id[1]);         pthread_create(&wwid[2],NULL,westWard,&id[2]);         pthread_create(&wwid[3],NULL,westWard,&id[3]);         pthread_create(&wwid[4],NULL,westWard,&id[4]);         pthread_create(&wwid[5],NULL,westWard,&id[5]);         pthread_create(&wwid[6],NULL,westWard,&id[6]);         pthread_create(&wwid[7],NULL,westWard,&id[7]);         pthread_create(&wwid[8],NULL,westWard,&id[8]);         pthread_create(&wwid[9],NULL,westWard,&id[9]);      }     return 0; }  当然，上面的代码在执行过程中，还是会出现若干个id相同的猴子朝一个方向过桥，那是因为while死循环过程中，有id重名的线程在等待过桥，不过，这些线程的线程ID是不会重复的，毕竟他们还是不同的线程。 引用 13 楼 qq_36821517 的回复:[quote=引用 11 楼 zangfong 的回复:] void *westWard(void *ptr)函数中，第二个if if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过要改为if(countWest==0)//若是本方最后一只猴子，允许对方猴子通过 在windows下运行，结果看着挺正常，我在ubuntu虚拟机上跑，好像是有问题。估计是我代码的问题了，回头得先去补一下操作系统。。。 大神有什么方法可以解决嘛   我打算结问题了，就等一次正确的运行[/quote]

user: user-0
content: 引用 11 楼 zangfong 的回复:void *westWard(void *ptr)函数中，第二个if if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过要改为if(countWest==0)//若是本方最后一只猴子，允许对方猴子通过 在windows下运行，结果看着挺正常，我在ubuntu虚拟机上跑，好像是有问题。估计是我代码的问题了，回头得先去补一下操作系统。。。 大神有什么方法可以解决嘛   我打算结问题了，就等一次正确的运行

user: user-0
content: 引用 4 楼 zangfong 的回复:对比一下你提供的算法，那你的eastWard和westWard函数中的是不是解错锁了     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mtWestWard); North() {     P(mutex_north)     if(count==0)         P(bridge)// 将桥上锁         count++;     V(mutex_north)      //go though the bridge...      P(mutex_north)      count--;      if(count==0)         V(brigde);//// 将桥解锁      V(mutex_north) }  根据你提供的算法，整理的代码如下，供参考： #include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> #include<windows.h> #define N_MONKEY 10 pthread_t ewid[N_MONKEY],wwid[N_MONKEY]; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//东西互斥 pthread_mutex_t mtEastWard=PTHREAD_MUTEX_INITIALIZER;//向东计数互斥 pthread_mutex_t mtWestWard=PTHREAD_MUTEX_INITIALIZER;//向西计数互斥 int countEast = 0; int countWest = 0; int i = 0;  void crossEastWard() {     printf("第%d只猴子向东======>>      \n",i+1); }  void crossWestWard() {     printf("      <<======第%d只猴子向西\n",i+1); }  void *eastWard(void *ptr) {      pthread_mutex_lock(&mtEastWard);     if(countEast==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);         countEast++;          pthread_mutex_unlock(&mtEastWard);          crossEastWard();//猴子通过          pthread_mutex_lock(&mtEastWard);         countEast--;     }     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);     pthread_mutex_unlock(&mtEastWard);     Sleep(2000);       pthread_exit((void *) 0); }  void *westWard(void *ptr) {      pthread_mutex_lock(&mtWestWard);      if(countWest==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);         countWest++;          pthread_mutex_unlock(&mtWestWard);          crossWestWard();//猴子通过          pthread_mutex_lock(&mtWestWard);          countWest--;     }     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);     pthread_mutex_unlock(&mtWestWard);     Sleep(2000);      pthread_exit((void *) 0); }   int main() {     while(1)     {         for(i = 0; i < N_MONKEY; i++)         {             pthread_create(&ewid[i],NULL,eastWard,&i);          }         for(i = 0; i < N_MONKEY; i++)         {              pthread_create(&wwid[i],NULL,westWard,&i);         }     }     return 0; }  根据你的代码我也跑了试一下，但是结果还是有问题，感觉应该除开算法其他地方也有问题，感觉和while函数有关，可以帮忙看一下主函数嘛  这是运行结果：

user: user-0
content: 引用 11 楼 zangfong 的回复:void *westWard(void *ptr)函数中，第二个if if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过要改为if(countWest==0)//若是本方最后一只猴子，允许对方猴子通过 在windows下运行，结果看着挺正常，我在ubuntu虚拟机上跑，好像是有问题。估计是我代码的问题了，回头得先去补一下操作系统。。。   嗯呐，在Ubuntu下面跑了还是这样，改了上面说的  if(countWest==0)//若是本方最后一只猴子，允许对方猴子通过

user: user-2
content: 楼上这个整理的挺好。只是有一点儿疑惑：      if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);     pthread_mutex_unlock(&mtEastWard);  这个解锁顺序好像不太对？不是很肯定。

user: user-1
content: void *westWard(void *ptr)函数中，第二个if if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过要改为if(countWest==0)//若是本方最后一只猴子，允许对方猴子通过 在windows下运行，结果看着挺正常，我在ubuntu虚拟机上跑，好像是有问题。估计是我代码的问题了，回头得先去补一下操作系统。。。

user: user-0
content: 引用 9 楼 user-2 的回复:[quote=引用 7 楼 user-2 的回复:]  volatile int countEast = 0; volatile int countWest = 0;  这两个变量应该用volatile修饰。不然会有这个问题。 还有一个就是你好像用了第三方的pthread库，这样的话连接的pthread库要和visual studio编译选择的库一致。查一下 (/MT) /MD /MDd /MTd的说明。 [/quote] 是这样的，我在Ubuntu的环境下编译运行的，编译器是gcc

user: user-2
content: 引用 7 楼 user-2 的回复: volatile int countEast = 0; volatile int countWest = 0;  这两个变量应该用volatile修饰。不然会有这个问题。 还有一个就是你好像用了第三方的pthread库，这样的话连接的pthread库要和visual studio编译选择的库一致。查一下 (/MT) /MD /MDd /MTd的说明。

user: user-0
content: 引用 7 楼 user-2 的回复: volatile int countEast = 0; volatile int countWest = 0;  这两个变量应该用volatile修饰。不然会有这个问题。  加上volatile修饰后还是会有这种情况呃

user: user-2
content: volatile int countEast = 0; volatile int countWest = 0;  这两个变量应该用volatile修饰。不然会有这个问题。

user: user-1
content: 对比一下你提供的算法，那你的eastWard和westWard函数中的是不是解错锁了     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mtWestWard); North() {     P(mutex_north)     if(count==0)         P(bridge)// 将桥上锁         count++;     V(mutex_north)      //go though the bridge...      P(mutex_north)      count--;      if(count==0)         V(brigde);//// 将桥解锁      V(mutex_north) }  根据你提供的算法，整理的代码如下，供参考： #include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> #include<windows.h> #define N_MONKEY 10 pthread_t ewid[N_MONKEY],wwid[N_MONKEY]; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//东西互斥 pthread_mutex_t mtEastWard=PTHREAD_MUTEX_INITIALIZER;//向东计数互斥 pthread_mutex_t mtWestWard=PTHREAD_MUTEX_INITIALIZER;//向西计数互斥 int countEast = 0; int countWest = 0; int i = 0;  void crossEastWard() {     printf("第%d只猴子向东======>>      \n",i+1); }  void crossWestWard() {     printf("      <<======第%d只猴子向西\n",i+1); }  void *eastWard(void *ptr) {      pthread_mutex_lock(&mtEastWard);     if(countEast==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);         countEast++;          pthread_mutex_unlock(&mtEastWard);          crossEastWard();//猴子通过          pthread_mutex_lock(&mtEastWard);         countEast--;     }     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);     pthread_mutex_unlock(&mtEastWard);     Sleep(2000);       pthread_exit((void *) 0); }  void *westWard(void *ptr) {      pthread_mutex_lock(&mtWestWard);      if(countWest==0) //若是本方第一只猴子，阻止对方猴子通过     {         pthread_mutex_lock(&mutex);         countWest++;          pthread_mutex_unlock(&mtWestWard);          crossWestWard();//猴子通过          pthread_mutex_lock(&mtWestWard);          countWest--;     }     if(countEast==0)//若是本方最后一只猴子，允许对方猴子通过         pthread_mutex_unlock(&mutex);     pthread_mutex_unlock(&mtWestWard);     Sleep(2000);      pthread_exit((void *) 0); }   int main() {     while(1)     {         for(i = 0; i < N_MONKEY; i++)         {             pthread_create(&ewid[i],NULL,eastWard,&i);          }         for(i = 0; i < N_MONKEY; i++)         {              pthread_create(&wwid[i],NULL,westWard,&i);         }     }     return 0; }

user: user-0
content: 引用 1 楼 user-2 的回复:这就是是一个简单的互斥问题，你咋做这么复杂？绳子是临界资源。不管哪一方的猴子要通过，先利用p操作拿到绳子就可以通过了。本次要通过的猴子完成后，释放绳子(v操作）。  他的算法是这个样子的   //可以过多辆车  semaphore bridge=1;//桥的信号量  semaphore mutex_north=1;//互斥信号量  semophore mutex_south=1;  int count=0;  North() {      P(mutex_north)       if(count==0)      P(bridge)// 将桥上锁         count++;       V(mutex_north)              go though the bridge...       P(mutex_north)       count--;      if(count==0)       V(brigde);       V(mutex_north) }    South() {      P(mutex_south)       if(count==0)        P(bridge)      count++;      V(mutex_south)       go through the bridge...      P(mutex_south)      count--;      if(count==0)       V(bridge)      V(mutex_south)  }

user: user-0
content: 他的算法是这个样子的   //可以过多辆车  semaphore bridge=1;//桥的信号量  semaphore mutex_north=1;//互斥信号量  semophore mutex_south=1;  int count=0;  North() {      P(mutex_north)       if(count==0)      P(bridge)// 将桥上锁         count++;       V(mutex_north)              go though the bridge...       P(mutex_north)       count--;      if(count==0)       V(brigde);       V(mutex_north) }    South() {      P(mutex_south)       if(count==0)        P(bridge)      count++;      V(mutex_south)       go through the bridge...      P(mutex_south)      count--;      if(count==0)       V(bridge)      V(mutex_south)  }

user: user-2
content: 这就是是一个简单的互斥问题，你咋做这么复杂？绳子是临界资源。不管哪一方的猴子要通过，先利用p操作拿到绳子就可以通过了。本次要通过的猴子完成后，释放绳子(v操作）。