一、线程的创建与退出
1.1 线程的创建
主线程
子线程
1.2 线程的退出
- 线程的退出可以使用
return
的方式——
线程使用return退出
但不可以使用exit()来退出当前线程
,因为exit是用来退出一个进程的,调用它不仅会退出当前线程,还会把其他所有线程都退出。
- 线程的退出还可以使用
pthread_exit()
来实现——
pthread_exit()
1.3 线程的回收
- 使用
pthread_join()
函数 (使用主线程
来回收子线程资源
)
使用pthread_join()
主线程会等子线程打印完后才开始打印主线程自己的内容——
图示代码
【使用主线程
来回收子线程资源
】的坏处是主线程处于阻塞状态
,就不能做其他事情了。
- 使用
pthread_detach()
函数 (线程
自己回收自己)
分离态
当一个线程被设置为分离态后,就不要在主线程中尝试回收子线程的TCB了。
分离态
的结果——
主线程和子线程交替打印
1.4 线程的取消
pthread_cancel()
实现取消一定要有系统调用(这样才能跑到内核态,不然一直在用户态),不然是不能实现取消的,只是一个标记的作用。
重点是系统调用开销很大,所以为了可以取消成功,我们可以使用
图示代码pthread_testcancel()
,它的开销很小。
二、线程属性
2.1 线程的栈大小
三、互斥锁
同步——不是同时
,而是有序地
去执行,排队执行
等待
前面的线程执行完了
才执行当前线程
异步——更相当于同时
(并发执行,不受限制)
我们之前写的
裸的多线程
就是异步
的
同步
是在需要访问公共资源(比如全局变量)
的情况下,一定要同步
3.1 线程不安全的实现
主线程做的就是等待
2个子线程的退出——
图示代码
3.2 阻塞式上锁
- lock()和unlock()
阻塞上锁
-
阻塞式上锁2——
图示代码
结果——
始终是一个线程拿到锁
可以看到,一直是一个线程拿到锁,另一个线程拿不到——
原因是假如
线程A
拿到锁以后,线程B
就会阻塞,那么当线程A释放锁以后,线程B还需要【唤醒】恢复现场
来抢锁,这个过程也是需要时间
的,这个间隔
线程A就会重新拿到这个锁,如此反复,就一直是线程A拿到锁。
- 解决方案(让之前拿到锁的
线程A阻塞
一下)——
让之前拿到锁的线程A阻塞一下
结果
2个线程交替地抢锁,没有抢到锁的线程就
阻塞等待
3.3 非阻塞式上锁
也叫非阻塞轮询
- trylock(),一个线程如果没有抢到锁并不会处于阻塞状态,而是会返回一个
错误码EBASY
。那么如果没有抢到这把锁,完全可以去做其他事情。
对应代码——
图示代码
结果——
图示结果
四、死锁
4.1 死锁的产生
可以看到,线程A拿到了锁0,准备拿锁1,但是锁1已经被线程B拿到了,线程B准备拿锁0,二者互相等待,就是死锁。(原因是嵌套
使用了锁,所以避免死锁的方法就是不要嵌套使用锁)
4.2 哪些操作下的全局变量不需要加锁
原子操作,比如a = 10
,这就是个原子操作,但是a++
不是
a++
(非原子操作)——
- 把a从
内存
中取出来放到寄存器
中(因为内存
中是不能进行加减的,寄存器可以)- 在
寄存器
中进行加1操作- 把
寄存器
中a新的内容
写回到地址空间
中
a = b
(非原子操作)——
- 从b中取出值放到
寄存器
中- 在
寄存器
中,将这个值添到a
中去
a = 10
是因为,10是一个立即数
,不用取,可以直接填到a的地址
中去
4.3 long long操作下的赋值
是否是原子操作
32位机中,寄存器可以容纳的数也是32位
,而long long的大小是64位
的,所以赋值会分成2次
,那么就不是一个原子操作
long long型整数的赋值分为高位
和低位
——
long long分为高4字节和低4字节
所以32位机中,long long类型的也要
加锁
但是在64位机中,long long
就可以一次赋值完
五、读写锁
5.1 读写锁的用处
- 多个线程
同时读
一段数据是不会引起竞争风险
的,写
的时候会产生冲突
写
的时候不允许读
,保证只有一个线程
可以写
- 无脑加入
互斥锁
,会导致性能降低(因为使用线程
就是为了提高并发性
) - 同时去读的情况
不能让每个线程都加一个
互斥锁
,因为这样就不能【多个线程同时读一块资源】(损害了并发性
),所以出现了读写锁
- 读写锁:可以多个读,只能1个写,写的时候不能读
阻塞与非阻塞加锁
- 多个线程读取数据的时候,如果有线程正在写,也是不能读的,需要等到它写完
六、条件变量
-
抢到锁的线程
可以在条件变量
上等待,等待的时候会把锁
交出来。那么另一个线程就可以拿到锁,但是也依然会发现条件不满足,然后第二个线程也在这个条件变量中等待,同时也把锁较出来
唤醒的是
线程
-
pthread_cond_wait(&cond, &mutex)
可能有2种状态,第一种是等待在条件变量上,第二种是条件变量被满足后多个线程之间还要争抢互斥锁(可能有多个线程等待在条件变量上)
重点是
抢到锁后
,还是要再判断一次是否满足条件
,满足才能继续执行下面的代码
,不然继续等着
6.1 条件变量的使用
线程想要执行代码需要满足2个条件,而不是1个条件——
- 满足条件
拿到锁
注意使用while而不是if来判断条件是否成立
如果用if判断条件是否成立,会出现一个Bug——两个线程第一次都满足了条件,但是线程A拿到了锁,所以往下执行,线程B没有拿到锁,所以阻塞,等到线程A释放锁的时候,线程B拿到锁然后执行。但是线程B这个时候可能已经不满足条件了(有锁的情况下),所以需要while循环判断条件是否满足。
网友评论