线程的创建销毁与退出,线程属性,互斥锁,读写锁

2020-12-02  本文已影响0人  StevenHD

一、线程的创建与退出

1.1 线程的创建

主线程
子线程

1.2 线程的退出

线程使用return退出
但不可以使用exit()来退出当前线程,因为exit是用来退出一个进程的,调用它不仅会退出当前线程,还会把其他所有线程都退出。
pthread_exit()

1.3 线程的回收

使用pthread_join()

主线程会等子线程打印完后才开始打印主线程自己的内容——

图示代码
【使用主线程来回收子线程资源】的坏处是主线程处于阻塞状态,就不能做其他事情了。
分离态

当一个线程被设置为分离态后,就不要在主线程中尝试回收子线程的TCB了。

分离态的结果——

主线程和子线程交替打印

1.4 线程的取消

pthread_cancel()实现取消一定要有系统调用(这样才能跑到内核态,不然一直在用户态),不然是不能实现取消的,只是一个标记的作用。

重点是系统调用开销很大,所以为了可以取消成功,我们可以使用pthread_testcancel(),它的开销很小。

图示代码

二、线程属性

2.1 线程的栈大小

三、互斥锁

同步——不是同时,而是有序地去执行,排队执行

等待前面的线程执行完了才执行当前线程

异步——更相当于同时(并发执行,不受限制)

我们之前写的裸的多线程就是异步

同步是在需要访问公共资源(比如全局变量)的情况下,一定要同步

3.1 线程不安全的实现

主线程做的就是等待2个子线程的退出——

图示代码

3.2 阻塞式上锁

阻塞上锁

原因是假如线程A拿到锁以后,线程B就会阻塞,那么当线程A释放锁以后,线程B还需要【唤醒】恢复现场来抢锁,这个过程也是需要时间的,这个间隔线程A就会重新拿到这个锁,如此反复,就一直是线程A拿到锁。

2个线程交替地抢锁,没有抢到锁的线程就阻塞等待

3.3 非阻塞式上锁

也叫非阻塞轮询

图示代码

结果——

图示结果

四、死锁

4.1 死锁的产生

可以看到,线程A拿到了锁0,准备拿锁1,但是锁1已经被线程B拿到了,线程B准备拿锁0,二者互相等待,就是死锁。(原因是嵌套使用了锁,所以避免死锁的方法就是不要嵌套使用锁)

4.2 哪些操作下的全局变量不需要加锁

原子操作,比如a = 10,这就是个原子操作,但是a++不是

a++(非原子操作)——

  1. 把a从内存中取出来放到寄存器中(因为内存中是不能进行加减的,寄存器可以)
  2. 寄存器中进行加1操作
  3. 寄存器a新的内容写回到地址空间

a = b(非原子操作)——

  1. 从b中取出值放到寄存器
  2. 寄存器中,将这个值添到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 读写锁的用处

的时候不允许,保证只有一个线程可以写

不能让每个线程都加一个互斥锁,因为这样就不能【多个线程同时读一块资源】(损害了并发性),所以出现了读写锁

阻塞与非阻塞加锁

六、条件变量

唤醒的是线程

重点是抢到锁后,还是要再判断一次是否满足条件,满足才能继续执行下面的代码,不然继续等着

6.1 条件变量的使用

线程想要执行代码需要满足2个条件,而不是1个条件——

  1. 满足条件
  2. 拿到锁


    注意使用while而不是if来判断条件是否成立

    如果用if判断条件是否成立,会出现一个Bug——两个线程第一次都满足了条件,但是线程A拿到了锁,所以往下执行,线程B没有拿到锁,所以阻塞,等到线程A释放锁的时候,线程B拿到锁然后执行。但是线程B这个时候可能已经不满足条件了(有锁的情况下),所以需要while循环判断条件是否满足。

上一篇下一篇

猜你喜欢

热点阅读