互斥机制的原因:
线程的抢占式执行,引起线程安全问题(多线程环境下程序的执行结果出现预期结果之外的值
)。主要在公共资源的使用上才会显示最大问题。多个线程访问的那个公共资源叫做——临界资源
访问临界资源的代码叫做——临界区
在临界区使用 互斥机制 就能解决线程不安全问题。
互斥机制的使用:使用互斥锁(互斥锁就像ATM取钱)
- 先加锁
- 执行临界区代码
- 释放锁
同一时刻只有一个线程才能执行临界区代码,其他线程只能等待锁的释放。
互斥锁相关函数:
位置在主函数中:
pthread_mutex_init(&mutex,NULL):初始化锁,一定要初始化
pthread_mutex_destory(&mutex):释放锁
定义全局函数位置定义:
pthread_mutex_t mutex:定义一个互斥锁
需要互斥锁就加在需要函数的位置中:
pthread_mutex_lock(&mutex):上锁
pthread_mutex_unlock(&mutex):开锁
互斥锁也叫挂起等待锁,一旦线程获取锁失败就会挂起(进入操作系统的等待队列中)这个线程什么时候才能恢复执行,也不是其他线程释放锁立即就能恢复执行,而是在其他线程释放锁之后,当前线程还要看操作系统的心情来决定啥时候恢复执行。
代码实例:
//多线程求g_count 的值 #include <stdio.h> #include<pthread.h> //头文件 #define THERAD_NUM 2 //定义一个互斥锁 pthread_mutex_t mutex; int g_count = 0; void* ThreadEntry(void* arg) { //新线程入口,参数 (void)arg; for (int i = 0; i < 50000;++i) { //如果锁已经被其他线程获取到了,当线程在想获取,就会在lock 函数阻塞 pthread_mutex_lock(&mutex);//上锁 ++g_count; pthread_mutex_unlock(&mutex); //开锁 } return NULL; } int main() { //主线程 pthread_t tid[THERAD_NUM]; pthread_mutex_init(&mutex, NULL);//互斥锁初始化函数 for (int i = 0; i < THERAD_NUM; ++i) { pthread_create(&tid[i], NULL, ThreadEntry, &args[i]); } for (int i = 0; i < 50000; ++i) { ++g_count; } for (int i = 0; i < THERAD_NUM; ++i) { //释放新线程 pthread_join(tid[i], NULL); } printf("%d", g_count); pthread_mutex_destory(&mutex);//互斥锁释放函数 return 0; }
结果就是:1000000;如果不加互斥锁结果就是未知,随意的,偶尔得到100000.
互斥锁能够保证线程的安全,最终的程序效率会有影响,但是会引发新的问题,更严重的问题——死锁 !!!
死锁
死锁的常见的场景:
- 一个线程连续两次的加锁(加了一次锁,再次尝试加锁)
- 两个线程1、2;两把锁A、B;线程1 先获取锁A,在获取锁B;线程2 先获取锁B,再获取锁B;就会引起死锁
(第二个就是:你拿着筷子,你朋友拿着勺子,但是你要勺子,朋友要筷子。这时候,你说:你先把勺子给我我在给你筷子。你朋友说:你先把筷子给我我在给你勺子。两个人互不相让,这时候就陷入死锁了。——类似哲学家吃饭问题,经典的死锁场景)
如何解决死锁问题?
实用操作时一个简单粗暴的方法,但是很有效。