一、相关概念:
临界资源:多个进程能够访问的资源
临界区:访问临界资源的一段代码
互斥:独占临界资源
同步:带着顺序性的进程运行,(大部分)建立在互斥的情况下
二元信号量:相当于一把 互斥锁
二、线程互斥
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; }
运行结果:
... ...
... ...
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; }
运行结果:
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); //通知至少一个等待的线程继续执行