专题-多线程

                                                               多线程

1.     为什么需要同步与互斥

都需要访问/使用同一种资源;

多个任务之间有依赖关系,某个任务的运行依赖于另一个任务。

2.     同步

按照预定的先后顺序运行,比如A 任务的运行依赖于 B 任务产生的数据。

3.     互斥

一个公共资源同一时刻只能被一个进程或者线程使用,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段当某个人物运行其中的

4.     互斥锁

#include <mutex>

互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

特点:原子性,唯一性,非繁忙等待

非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

5.     条件变量(同步)

#include <condition_variable>

常用接口:

notify_one()就是唤醒处于wait中的其中一个(随机)条件变量(可能当时有很多条件变量都处于wait状态)。

notify_all()(唤醒所有等待的线程)

wait()可以让线程陷入休眠状态。

在wait()函数之前,使用互斥锁保护了,如果wait的时候什么都没做,岂不是一直持有互斥锁?那生产者也会一直卡住,不能够将数据放入队列中了。所以,wait()函数会先调用互斥锁的unlock()函数,然后再将自己睡眠,在被唤醒后,又会继续持有锁,保护后面的队列操作。而lock_guard没有lock和unlock接口,而unique_lock提供了。这就是必须使用unique_lock的原因。

6.     伪唤醒

wait()从阻塞到返回,不一定就是由于notify_one()函数造成的,还有可能由于系统的不确定原因唤醒(可能和条件变量的实现机制有关),这个的时机和频率都是不确定的,被称作伪唤醒,如果在错误的时候被唤醒了,执行后面的语句就会错误,所以需要再次判断队列是否为空,如果还是为空,就继续wait()阻塞。

因此,wait()前面的条件判断需要用while

7.     读写锁

使用场景:假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。

读写锁可以有3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态。

特点:

如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。

如果有其它线程写数据,则其它线程都不允许读、写操作。

8.     自旋锁

而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。

自旋锁的使用场景:锁的持有时间比较短

9.     信号量

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

10.  创建线程

thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行

11.  线程等待(同步)

th1.join();  //主线程等待直到该子线程执行结束

12.  线程分离

th1.detach();  //将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。

13.  僵尸进程

即子进程先于父进程退出后,子进程的PCB需要其父进程释放,但是父进程并没有释放子进程的PCB,这样的子进程就称为僵尸进程。

14.  孤儿进程

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

猜你喜欢

转载自www.cnblogs.com/XZDSF/p/11349098.html