一、相关概念

     临界资源:多个进程能够访问的资源

     临界区:访问临界资源的一段代码

     互斥:独占临界资源

     同步:带着顺序性的进程运行,(大部分)建立在互斥的情况下

     二元信号量:相当于一把 互斥锁  

二、线程互斥

1、造成干扰:进程进行均匀切换

2、互斥量mutex):

   加锁:变为原子

   返回值:成功 0;失败 错误号。

    pthread_mutex_t   mutex = PTHREAD_MUTEX_INITIALIZER;  //定义锁  

        关于安全、性能等    注意:加锁的位置

    int pthread_mutex_init(pthread_mutex_t *restrict mutex,

              const pthread_mutexattr_t *restrict attr);  //对 mutex 初始化

                // arr:设定mutex属性

    int pthread_mutex_destroy(pthread_mutex_t *mutex);  //销毁锁

         int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁  获取mutex

         int pthread_mutex_trylock(pthread_mutex_t *mutex); // 获锁但并不挂起等待。  失败 返回 EBUSY.

    int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁

 

     若mutex=1,则互斥锁 空闲,调用lock 获取锁;若mutex=0,则表示锁已被另一线程获取,调用lock需挂起等待。

#include<stdio.h>
#include<pthread.h>
static int g_count=0;
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; //lock
void* print_bug(void *arg)
{
	int tmp=0,index=0;

	while(index++ < 100)
	{
		tmp=g_count;
		printf("this is thread:%d,g_count:%d\n",(int)arg,tmp++);
		g_count=tmp;
	}
}

int main()
{
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,print_bug,(void*)1);
	pthread_create(&tid2,NULL,print_bug,(void*)2);
	
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	printf("res:%d\n",g_count);
	pthread_mutex_destroy(&lock);
	return 0;
}

运行结果:

            ... ...

    wKioL1cXP_KAlCOUAAAzAMuKpME947.jpg

            ... ...

    wKiom1cXPvKBL9wOAAAPWq5ua7Q450.jpg

3、死锁:多个执行流因为执行的顺序、方式等需要对方的锁,而对方的锁都被占用,使陷入挂起等待状态。

   1)死锁类型

    (1)一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此 就永远处于挂起等待状态了,这叫做死锁(Deadlock)。

    (2)线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放 锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都 永远处于挂起状态了。

   2)死锁必要条件:1、互斥条件(在一段时间内某线程独占该资源);

         2、请求和保持条件(该线程已保持至少一个资源,但又有新的资源请求,而该资源又已被占有,此时请求线程阻塞,但又对自己所占资源保持不变);

         3、不剥夺条件(线程已获得的资源,在未被使用完之前,不能被剥夺,只能在使用完时由自己释放);

         4、环路等待条件(在发生死锁时,必然存在一个线程--资源的环形链)。

#include<stdio.h>
#include<pthread.h>
static int g_count=0;
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; //lock
void* print_bug(void *arg)
{
	int tmp=0,index=0;

	while(index++ < 100)
	{
		pthread_mutex_lock(&lock); //uselock
		tmp=g_count;
		printf("this is thread:%d,g_count:%d\n",(int)arg,tmp++);
		g_count=tmp;
		pthread_mutex_lock(&lock); //unlock
	}
}

int main()
{
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,print_bug,(void*)1);
	pthread_create(&tid2,NULL,print_bug,(void*)2);
	
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	printf("res:%d\n",g_count);
	pthread_mutex_destroy(&lock);
	return 0;
}

运行结果:

    wKioL1cXQe3QZaZzAAAeUuLg8t4787.jpg


   3)避免死锁的原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。

     如果要为所有的锁确定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock 调用,以免死锁。

     

三、线程同步 ---- 协同完成

     条件变量Condition Variable):线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。

     在pthread库中通过 条件变量 来阻塞等待一个条件,或者唤醒等待这个条件的线程。

     Condition Variable 用 pthread_cond_t 类型的变量表示,可以这样 初始化和销毁.

     返回值:成功返回0,失败返回错误号。

     

     int pthread_cond_destroy(pthread_cond_t *cond);

     int pthread_cond_init(pthread_cond_t *restrict cond,

              const pthread_condattr_t *restrict attr);

     pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

        int pthread_cond_timedwait(pthread_cond_t *restrict cond,

              pthread_mutex_t *restrict mutex,

              const struct timespec *restrict abstime); //条件不成熟,等待

        int pthread_cond_wait(pthread_cond_t *restrict cond,

              pthread_mutex_t *restrict mutex);

    int pthread_cond_broadcast(pthread_cond_t *cond);  //唤醒等待的线程

    int pthread_cond_signal(pthread_cond_t *cond);  //通知至少一个等待的线程继续执行