美文网首页
线程的创建销毁与退出,线程属性,互斥锁,读写锁

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

作者: StevenHD | 来源:发表于2020-12-02 18:18 被阅读0次

一、线程的创建与退出

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++(非原子操作)——

  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 读写锁的用处

  • 多个线程同时读一段数据是不会引起竞争风险的,的时候会产生冲突

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

  • 无脑加入互斥锁,会导致性能降低(因为使用线程就是为了提高并发性)
  • 同时去读的情况

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

  • 读写锁:可以多个读,只能1个写,写的时候不能读
阻塞与非阻塞加锁
  • 多个线程读取数据的时候,如果有线程正在写,也是不能读的,需要等到它写完

六、条件变量

  • 抢到锁的线程可以在条件变量上等待,等待的时候会把交出来。那么另一个线程就可以拿到锁,但是也依然会发现条件不满足,然后第二个线程也在这个条件变量中等待,同时也把锁较出来

唤醒的是线程

  • pthread_cond_wait(&cond, &mutex)可能有2种状态,第一种是等待在条件变量上,第二种是条件变量被满足后多个线程之间还要争抢互斥锁(可能有多个线程等待在条件变量上)

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

6.1 条件变量的使用

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

  1. 满足条件
  2. 拿到锁


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

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

相关文章

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

    一、线程的创建与退出 1.1 线程的创建 主线程 子线程 1.2 线程的退出 线程的退出可以使用return的方式...

  • 可重入读写锁 ReentrantReadWriteLock

    读写锁分为读锁和写锁,多个线程获取读锁不互斥,读写锁、写写锁互斥。 输出

  • 线程同步与互斥

    Linux--线程编程 多线程编程-互斥锁 线程同步与互斥 互斥锁 信号量 条件变量 互斥锁 互斥锁的基本使用...

  • 24.读写锁

    读写锁(ReentrantReadWriteLock)就是读线程和读线程之间不互斥。读读不互斥,读写互斥,写写互斥...

  • 阿里、网易和腾讯面试题 C/C++

    一、线程、锁 1、Posix Thread互斥锁 线程锁创建 a.静态创建 pthread_mutex_tmute...

  • ReentrantReadWriteLock用法

    读读锁不互斥 结果如下,读读锁不互斥,三个线程同时结束 写写锁互斥 可以看到三个线程依次完成 读写互斥 可以看到和...

  • iOS底层探索-多线程锁

    多线程的锁大致可分为两大类:互斥锁、自旋锁;也可以分为三类:互斥锁、自旋锁、读写锁。 一、互斥锁:互斥+同步(强调...

  • Linux下多线程编程详解

    简介 线程创建 线程属性设置 线程参数传递 线程优先级 线程的数据处理 线程的分离状态 互斥锁 信号量 一 线程创...

  • 线程通信

    线程来说:共享变量 + 锁机制、 信号机制(线程的同步)、 读写锁:允许多个线程同时读共享数据,而对写操作互斥。 ...

  • 守护线程/线程互斥锁

    守护线程 线程互斥锁

网友评论

      本文标题:线程的创建销毁与退出,线程属性,互斥锁,读写锁

      本文链接:https://www.haomeiwen.com/subject/baeywktx.html