C++11 多线程

小彭老师的课程

c++多线程

没有多线程的时候,在执行download的时候会卡住


在引入多线程之后,就可以在另一个线程中执行download,而在主线程中执行interact

join函数的作用

没有join的话,子线程可能会由于主线程退出而被迫退出

所以可以调用t1.join()等待子线程结束,主线程才结束

detach函数

当一个线程在函数中创建时,当函数结束时,该线程也会被解构,那么这个时候该线程还没被执行完,错误了

所以这个时候可以用detach函数(也可以在函数体里头join),detach函数与join的区别thread对象可以想象成一根指针,那么当调用detach的时候,该thread对象就是nonjoinable了,就不会报exception了

但是这个时候仍然存在:主进程退出,相关的所有线程都会退出的情况,这时候可以创建一个线程池(在main函数里join全部的线程)
C++20自动join所有子线程

一个线程调用joinable()函数判断该线程是否可以被join或者detach

异步

std::async产生一个带返回值的thread
std::async的get像是带返回值的join,而wait像是join
wait_for与status结合判断线程是否执行结束

用std::launch::deferred实现惰性求值

std::async 的底层实现:std::promise
std::shared_future

互斥量

std::lock_guard的上🔒与解🔒符合RAII思想,离开for的花括号就会自动解锁,其实这个lock_guard的实现就是一个class,在每次进入for的{}时候创建一个object,那么调用构造函数,其中就是mutex.lock(),而退出{}时,调用析构,析构便会调用mutex.unlock()

std::unique_lock自由度更高

自由度高到甚至可以延迟上🔒

多个对象可以多个🔒

try_lock上🔒失败不等待

try_lock_for是设置等待时间

unique_lock可以用**std::try_to_lock **作为参数

std::adopt_lock

鸭子类型

死锁

ab型死锁

手动解决死锁


std::lock 解决死锁

符合RAII思想的std::scoped_lock

aa型死锁


手动解决aa型死锁

std::recursive_mutex标准库帮忙解决aa型死锁

线程安全的数据结构

封装一个安全的vector

mutable使得变量可以在const函数中修改

读写🔒

shared_mutex既可以调用lock()也可以调用lock_shared()

符合RAII思想的shared_mutex

条件变量

这里的cv.wait()表示没拿到cv,也就是假定共享变量一开始是0,
cv.wait()cv的等待队列中塞入一个线程,可以发现 std::condition_variable 必须和 std::unique_lockstd::mutex 一起用

这就是为什么 wait() 需要一个 unique_lock 作为参数,因为要保证多个线程被唤醒时,只有一个能够被启动。如果不需要,在 wait() 返回后调用 lck.unlock() 即可


甚至可以有一个额外的参数,来达到继续等到的目的,可以看到,第二次notify_one才把thread t1唤醒

notify_all可以唤醒多个等待者

原子操作

一条语句会被拆分为三条汇编

  • 加🔒暴力解决问题,但是慢
  • atomic解决

    但是counter = counter + 1不是原子的,因为编译器看不出来前后两个counter是同一个对象

    还可以调用与+=等价的函数fetch_add

    追加多个是啥意思?是指追加到当前元素的后n个位置

    atomic变量的exchange操作
    compare_exchange_strong,

    为啥CAS操作都是这样的,还要去改变别人传进来的值?难道是为了第二次获取🔒的时候能够获取到吗

posted @ 2022-03-22 09:48  抿了抿嘴丶  阅读(134)  评论(0编辑  收藏  举报