一、互斥條件
互斥條件指進程對所分配的資源進行排他性使用,即在一段時間內某資源只有一個進程使用。如果一個資源可以同時被多個進程使用,那麼死鎖就不會發生。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t forks[5]; //定義五個互斥量
void philosopher(void *arg)
{
int id = *((int*)arg);
int left = id;
int right = (id + 1) % 5;
while(1) {
printf("Philosopher %d is thinking.\n", id);
sleep(rand() % 5);
printf("Philosopher %d is hungry.\n", id);
pthread_mutex_lock(&forks[left]); //嘗試拿左手邊的叉子
printf("Philosopher %d get the fork %d.\n", id, left);
pthread_mutex_lock(&forks[right]); //嘗試拿右手邊的叉子
printf("Philosopher %d get the fork %d.\n", id, right);
printf("Philosopher %d is eating.\n", id);
sleep(rand() % 5);
pthread_mutex_unlock(&forks[left]); //放下左邊的叉子
pthread_mutex_unlock(&forks[right]); //放下右邊的叉子
}
}
int main()
{
pthread_t threads[5];
int ids[5] = {0, 1, 2, 3, 4};
//初始化互斥量
for(int i = 0; i < 5; ++i)
pthread_mutex_init(&forks[i], NULL);
//創建線程
for(int i = 0; i < 5; ++i)
pthread_create(&threads[i], NULL, (void*)philosopher, &ids[i]);
//等待線程結束
for(int i = 0; i < 5; ++i)
pthread_join(threads[i], NULL);
//銷毀互斥量
for(int i = 0; i < 5; ++i)
pthread_mutex_destroy(&forks[i]);
return 0;
}
在上述代碼中,每個哲學家都需要使用左右兩邊的叉子才能進行進餐,而每個叉子只能被一個哲學家使用,因此滿足了互斥條件。這種情況下,如果所有哲學家同時拿着自己左手邊的叉子等待右手邊的叉子,就會發生死鎖。
二、請求和保持條件
請求和保持條件指進程在請求資源時保持對已經佔有的資源的不釋放。如果一個進程在等待另一個進程佔有的資源時,自己還佔有一些資源,那麼請求和保持條件就成立,就有可能會發生死鎖。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex; //定義互斥量
int resource = 1; //初始值為1
void* thread1(void* arg)
{
while(1) {
pthread_mutex_lock(&mutex); //申請互斥鎖
printf("Thread1 gets the mutex.\n");
sleep(rand() % 5);
if(resource > 0) { //請求資源,但保持已經佔用的資源
resource--;
printf("Thread1 gets the resource, resource = %d.\n", resource);
}
pthread_mutex_unlock(&mutex); //釋放互斥鎖
sleep(rand() % 5);
}
}
void* thread2(void* arg)
{
while(1) {
pthread_mutex_lock(&mutex); //申請互斥鎖
printf("Thread2 gets the mutex.\n");
sleep(rand() % 5);
if(resource < 5) { //請求資源,但保持已經佔用的資源
resource++;
printf("Thread2 gets the resource, resource = %d.\n", resource);
}
pthread_mutex_unlock(&mutex); //釋放互斥鎖
sleep(rand() % 5);
}
}
int main()
{
pthread_t thread[2];
//初始化互斥量
pthread_mutex_init(&mutex, NULL);
//創建線程
pthread_create(&thread[0], NULL, thread1, NULL);
pthread_create(&thread[1], NULL, thread2, NULL);
//等待線程結束
pthread_join(thread[0], NULL);
pthread_join(thread[1], NULL);
//銷毀互斥量
pthread_mutex_destroy(&mutex);
return 0;
}
在上述代碼中,兩個線程都需要先申請到互斥鎖才能進行後續操作。線程1每次在獲得互斥鎖之後會嘗試獲取一個資源,但不釋放已經佔用的資源;線程2每次在獲得互斥鎖之後會嘗試釋放一個資源,但保持已經佔用的資源。這樣當線程1佔用了1個資源,線程2佔用了4個資源時,就會發生死鎖。
三、不剝奪條件
不剝奪條件指進程已經佔有的資源不能被剝奪,只能由該進程自己釋放。如果一個進程已經佔有某些資源,但是在等待另外一些資源的時候,被系統強制剝奪了它所佔有的資源,那麼就可能會發生死鎖。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex1, mutex2; //定義兩個互斥量
void* thread1(void* arg)
{
pthread_mutex_lock(&mutex1); //先申請鎖1
printf("Thread1 gets mutex1.\n");
sleep(rand() % 5);
pthread_mutex_lock(&mutex2); //再申請鎖2
printf("Thread1 gets mutex2.\n");
pthread_mutex_unlock(&mutex2); //釋放鎖2
printf("Thread1 releases mutex2.\n");
pthread_mutex_unlock(&mutex1); //釋放鎖1
printf("Thread1 releases mutex1.\n");
return NULL;
}
void* thread2(void* arg)
{
pthread_mutex_lock(&mutex2); //先申請鎖2
printf("Thread2 gets mutex2.\n");
sleep(rand() % 5);
pthread_mutex_lock(&mutex1); //再申請鎖1
printf("Thread2 gets mutex1.\n");
pthread_mutex_unlock(&mutex1); //釋放鎖1
printf("Thread2 releases mutex1.\n");
pthread_mutex_unlock(&mutex2); //釋放鎖2
printf("Thread2 releases mutex2.\n");
return NULL;
}
int main()
{
pthread_t thread[2];
//初始化互斥量
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
//創建線程
pthread_create(&thread[0], NULL, thread1, NULL);
pthread_create(&thread[1], NULL, thread2, NULL);
//等待線程結束
pthread_join(thread[0], NULL);
pthread_join(thread[1], NULL);
//銷毀互斥量
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
在上述代碼中,線程1先申請到互斥鎖1,再申請到互斥鎖2,線程2則相反。如果在程序運行過程中,系統剝奪某一個線程的其中一個鎖,那麼就會出現死鎖。
四、循環等待條件
循環等待條件指進程申請資源時形成了一個頭尾相接的循環等待鏈。這種情況下,每個進程都在等待一個資源而被其他進程佔用,從而形成了一個死循環。如果所有進程都按照一個確定的順序來申請資源,那麼循環等待條件就不會發生死鎖。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex[5]; //定義五個互斥量
void* thread(void* arg)
{
int id = *((int*)arg);
int prev = (id + 4) % 5; //前面一個線程的id
int next = (id + 1) % 5; //後面一個線程的id
//每個線程都先申請前面的互斥鎖
if(id < prev)
pthread_mutex_lock(&mutex[id]);
else {
pthread_mutex_lock(&mutex[prev]);
pthread_mutex_lock(&mutex[id]);
}
printf("Thread %d gets mutex %d.\n", id, prev);
sleep(rand() % 5);
//然後申請後面的互斥鎖
if(id < next)
pthread_mutex_lock(&mutex[id]);
else {
pthread_mutex_lock(&mutex[id]);
pthread_mutex_lock(&mutex[next]);
}
printf("Thread %d gets mutex %d.\n", id, id);
//釋放鎖
pthread_mutex_unlock(&mutex[id]);
printf("Thread %d releases mutex %d.\n", id, id);
if(id < prev) {
pthread_mutex_unlock(&mutex[prev]);
printf("Thread %d releases mutex %d.\n", id, prev);
}
else
pthread_mutex_unlock(&mutex[id-1]);
return NULL;
}
int main()
{
pthread_t threads[5];
int ids[5] = {0, 1, 2, 3, 4};
//初始化互斥量
for(int i = 0; i < 5; ++i)
pthread_mutex_init(&mutex[i], NULL);
//創建線程
for(int i = 0; i < 5; ++i)
pthread_create(&threads[i], NULL, thread, &ids[i]);
//等待線程結束
for(int i = 0; i < 5; ++i)
pthread_join(threads[i], NULL);
//銷毀互斥量
for(int i = 0; i < 5; ++i)
pthread_mutex_destroy(&mutex[i]);
return 0;
}
在上述代碼中,五個線程分別持有編號為0、1、2、3、4的互斥鎖,並且每個線程都先申請前面一個線程的互斥鎖,再申請自己的互斥鎖。這樣以來,如果五個線程按照一定的順序來啟動,死鎖就不會出現。
原創文章,作者:WRBVQ,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/333381.html