哲学家用餐模型分析

xiaoxiao2021-02-28  98

#include #include #include #include pthread_mutex_t m[5]; void *tfn(void *arg) { int i, l, r; srand(time(NULL)); i = (int)arg; if (i == 4) l = 0, r = i; else l = i; r = i+1; while (1) { pthread_mutex_lock(&m[l]); if (pthread_mutex_trylock(&m[r]) == 0) { printf("\t%c is eating \n", 'A'+i); pthread_mutex_unlock(&m[r]); } pthread_mutex_unlock(&m[l]); sleep(rand() % 5); } return NULL; } int main(void) { int i; pthread_t tid[5]; for (i = 0; i < 5; i++) pthread_mutex_init(&m[i], NULL); for (i = 0; i < 5; i++) pthread_create(&tid[i], NULL, tfn, (void *)i); for (i = 0; i < 5; i++) pthread_join(tid[i], NULL); for (i = 0; i < 5; i++) pthread_mutex_destroy(&m[i]); return 0; } 多线程版:

         选用互斥锁mutex,如创建5个, pthread_mutex_t m[5];

         模型抽象:    

                   5个哲学家 --> 5个线程;    5支筷子 --> 5把互斥锁                  int left(左手), right(右手)

                   5个哲学家使用相同的逻辑,可通用一个线程主函数,void *tfn(void *arg),使用参数来表示线程编号:int i = (int)arg;

                   哲学家线程根据编号知道自己是第几个哲学家,而后选定锁,锁住,吃饭。否则哲学家thinking。

                                                                                A  B   C   D   E

                   5支筷子,在逻辑上形成环:0   1  2   3   4   分别对应5个哲学家:

所以有:

                   if(i== 4)   

                            left= i, right = 0;

                   else

                            left= i, right = i+1;

         振荡:如果每个人都攥着自己左手的锁,尝试去拿右手锁,拿不到则将锁释放。过会儿五个人又同时再攥着左手锁尝试拿右手锁,依然拿不到。如此往复形成另外一种极端死锁的现象——振荡。

         避免振荡现象:只需5个人中,任意一个人,拿锁的方向与其他人相逆即可(如:E,原来:左:4,右:0 现在:左:0, 右:4)。

         所以以上if else语句应改为:

                   if(i== 4)   

                            left= 0, right = i;

                   else

                            left= i, right = i+1;

         而后,首先应让哲学家尝试加左手锁: 

while {

                            pthread_mutex_lock(&m[left]);     如果加锁成功,函数返回再加右手锁,

                                                                                             如果失败,应立即释放左手锁,等待。

                            若,左右手都加锁成功--> 吃 --> 吃完 --> 释放锁(应先释放右手、再释放左手,是加锁顺序的逆序)

                   }

         主线程(main)中,初始化5把锁,销毁5把锁,创建5个线程(并将i传递给线程主函数),回收5个线程。

避免死锁的方法:

         1. 当得不到所有所需资源时,放弃已经获得的资源,等待。

         2. 保证资源的获取顺序,要求每个线程获取资源的顺序一致。如:A获取顺序1、2、3;B顺序应也是1、2、3。若B为3、2、1则易出现死锁现象。                                                                                                      

多进程版

相较于多线程需注意问题:

         需注意如何共享信号量 (注意:坚决不能使用全局变量 sem_t s[5])

实现:

         main函数中:       

循环sem_init(&s[i], 0, 1); 将信号量初值设为1,信号量变为互斥锁。

                   循环 sem_destroy(&s[i]);

                   循环创建 5 个子进程。 if(i < 5) 中完成子进程的代码逻辑。

                   循环回收 5 个子进程。

         子进程中:

if(i == 4) 

left = 0, right == 4;

                   else 

left = i, right = i+1;  

                   while(1) {

                            使用sem_wait(&s[left]) 锁左手,尝试锁右手,若成功 --> 吃;若不成功 --> 将左手锁释放。

                            吃完后, 先释放右手锁,再释放左手锁。

                   }

【重点注意】:

直接将sem_t s[5]放在全局位置,试图用于子进程间共享是错误的!应将其定义放置与mmap共享映射区中。main中:

sem_t *s =mmap(NULL, sizeof(sem_t) * 5, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);

         使用方式:将s当成数组首地址看待,与使用数组s[5]没有差异。     

#include #include #include #include pthread_mutex_t m[5]; void *tfn(void *arg) { int i, l, r; srand(time(NULL)); i = (int)arg; if (i == 4) l = 0, r = i; else l = i; r = i+1; while (1) { pthread_mutex_lock(&m[l]); if (pthread_mutex_trylock(&m[r]) == 0) { printf("\t%c is eating \n", 'A'+i); pthread_mutex_unlock(&m[r]); } pthread_mutex_unlock(&m[l]); sleep(rand() % 5); } return NULL; } int main(void) { int i; pthread_t tid[5]; for (i = 0; i < 5; i++) pthread_mutex_init(&m[i], NULL); for (i = 0; i < 5; i++) pthread_create(&tid[i], NULL, tfn, (void *)i); for (i = 0; i < 5; i++) pthread_join(tid[i], NULL); for (i = 0; i < 5; i++) pthread_mutex_destroy(&m[i]); return 0; }

                                  

转载请注明原文地址: https://www.6miu.com/read-68724.html

最新回复(0)