线程安全
线程安全:多个执行流对同一临界资源进行争夺,但是不会造成数据二义或逻辑混乱
可重入: 同一个函数,多个执行流调用,一个执行流还没有结束,另一个执行流开始调用
不可重入:
- malloc/free函数
- 调用标准I/O库
- 函数体内有静态数据结构
函数可重入,线程安全
函数不可重入,多个线程调用,可能会引起线程安全问题
全局变量,即不可重入,也不线程安全
互斥
互斥:同一时间只能由一个线程能够访问临界资源
临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部,访问临界自娱的代码,就叫做临界区
原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成
写一个卖票的例子
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4
5 int tickets = 100;
6
7 void *GetTicket(void *args)
8 {
9 while(1)
10 {
11 if(tickets > 0)
12 {
13 usleep(1000000);
14 printf("get a ticket ...%d\n", tickets--);
15 }
16 else
17 {
18 printf("%s... quit\n", (char*)args);
19 break;
20 }
21 }
22 pthread_exit((void*)0);
23 }
24 int main()
25 {
26 pthread_t tid1,tid2,tid3,tid4;
27
28 pthread_create(&tid1, NULL, GetTicket, "thread 1");
29 pthread_create(&tid2, NULL, GetTicket, "thread 2");
30 pthread_create(&tid3, NULL, GetTicket, "thread 3");
31 pthread_create(&tid4, NULL, GetTicket, "thread 4");
32
33 pthread_join(tid1, NULL);
34 pthread_join(tid2, NULL);
35 pthread_join(tid3, NULL);
36 pthread_join(tid4, NULL);
37
38 return 0;
39 }
如图产生了负数,这明显不合理,是因为tickets是临界资源,同时被四个线程所访问,因为- -操作不是原子性(因为冯诺依曼,++,–操作最少要经历三步,一从内存把数据拿到CPU中,二执行操作,三数据从CPU写回内存),有可能一个线程正在访问的时候,一个线程正在写入。所以要对临界资源加锁。
pthread_mutex_t lock; //创建锁
pthread_mutex_lock(&lock);//加锁
pthread_mutex_unlock(&lock);//解锁
pthread_mutex_init(&lock, NULL);//初始化锁
pthread_mutex_destroy(&lock);//释放锁
互斥锁:
1. 互斥
2. 提供等待队列
同样的,一个线程要进行操作需要先得到锁,那么所有线程都会访问锁,锁也是临界资源,所以为了保证锁的原子性,大多数体系结构提供了swap或者exchange的汇编指令,由寄存器完成和内存的数据交换。
死锁:一组进程中各个进程均占有资源,但互相申请其他进程所占有的资源,且不释放自己的资源,相互等待。
条件:
- 互斥
- 请求与保持
- 不剥夺
- 循环等待
同步
通过条件判断实现对临界资源访问的合理性
详细会在我的另一篇博客中介绍:https://blog.csdn.net/weixin_43697119/article/details/103475885