继上次的创建线程后,我又带着一段新的代码来了QAQ
这个代码也和线程有关,pthread_create()函数我上次提到了,就不再赘述了,不知道的朋友可以百度或者看我的进程管理实验【2】。今天的代码不仅和线程有关,它其实描述的是死锁。
死锁是什么呢?
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
啥是互相竞争资源?我上次提到在操作系统中有一些资源在一个时间点上只能被一个进程访问,这些资源被称为临界资源,为了实现临界资源的同步访问,我们加了一个锁,当进程获得这个锁后它才可以访问临界资源,说到着,我们试着想一想若是一个资源必须获得两个锁才可以访问,当一个进程获得一个锁后,另一个进程恰巧获得了另一个锁,此时第一个进程得不到第二个锁,第二个进程得不到第一个锁,就产生了死锁。
我们来看一段代码:
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <pthread.h>
#define LOOP_TIMES 10000
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; //静态初始化互斥锁
void* thread_worker(void*);
void critical_section(int thread_num, int i);
int main(void)
{
int rtn, i;
pthread_t pthread_id = 0; /* 存放子线程的id */
rtn = pthread_create(&pthread_id, NULL, thread_worker, NULL ); //第一个null表示设置线程的属性,第二个null表示万能指针arg,thread_worker表示线程从thread_worker函数的地址开始运行
if(rtn != 0) //出错
{
printf("pthread_create ERROR!\n");
return -1;
}
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex1); //锁操作
pthread_mutex_lock(&mutex2); //锁操作
critical_section(1, i);
pthread_mutex_unlock(&mutex2); //解锁操作
pthread_mutex_unlock(&mutex1); //解锁操作
}
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2); //注销互斥锁
return 0;
}
void* thread_worker(void *p)
{
int i;
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
critical_section(2, i);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
}
void critical_section(int thread_num, int i)
{
printf("Thread%d:%d\n", thread_num, i);
}
前面的声明很简单,定义了一个循环次数和两个锁,我们直接看主程序:
rtn = pthread_create(&pthread_id, NULL, thread_worker, NULL ); //第一个null表示设置线程的属性,第二个null表示万能指针arg,thread_worker表示线程从thread_worker函数的地址开始运行
if(rtn != 0) //出错
{
printf("pthread_create ERROR!\n");
return -1;
}
从代码可以看出首先创建了一个线程,将它的id赋给了pthread_id,没有传参,线程执行thread_work()函数,若创建出错return。我们先来看线程执行了个啥?
void* thread_worker(void *p)
{
int i;
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
critical_section(2, i);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
}
这里其实就是线程通过同时获得两个锁之后访问临界区(访问临界资源的代码段),且这段代码被嵌在了循环中,再来看critical_section(2,i):
void critical_section(int thread_num, int i)
{
printf("Thread%d:%d\n", thread_num, i);
}
这句打印语句Thread%d其实想告诉我们传进去的2其实代表了这个进程的代号,那1呢?自然是主进程了,这句代码简单的打印了当前的循环次数。按照程序的走向,我们该看下面的代码了:
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex1); //锁操作
pthread_mutex_lock(&mutex2); //锁操作
critical_section(1, i);
pthread_mutex_unlock(&mutex2); //解锁操作
pthread_mutex_unlock(&mutex1); //解锁操作
}
啥都没变,基本和线程执行的代码一样,唯一有区别的是它拿锁的顺序不同,它先拿的1锁,而线程先拿2锁。其实程序挺简单的,到此我们可以预测一下程序的运行了:主进程和线程互相竞争资源来打印信息,所以开始会有一些信息被打印,到某一时刻时,主进程抢到了1锁,线程抢到了2锁导致系统死锁,程序进入假死状态。
我们来验证一下,为了看的更清楚为加两个打印语句:
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex1); //锁操作
printf("主进程抢到1锁\n") ;
pthread_mutex_lock(&mutex2); //锁操作
critical_section(1, i);
pthread_mutex_unlock(&mutex2); //解锁操作
pthread_mutex_unlock(&mutex1); //解锁操作
}
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex2);
printf("线程抢到2锁\n") ;
pthread_mutex_lock(&mutex1);
critical_section(2, i);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
好了,运行如下:
......一点问题也没有,有点不习惯??????哈哈哈,这样我就能睡觉了。0.0