1. 基本概念
1.1 并发
- 多个任务同时进行(假象)
- 目的:是提高效率
- 策略:是时间片轮询,上下文切换
- 单核
- 多核(硬件并发)
- 多进程并发,进程启动速度慢,资源占用高,进程间通信
- 多线程并发,线程启动速度快,资源占用低,共享内存地址空间,访问互斥
1.2 可执行程序
1.3 进程
1.4 线程
- 多线程优点:可以同时并发处理多个任务。
- 每个进程都有唯一的一个主线程,主线程的生命周期与进程一致,主线程执行完毕代表进程执行完毕。
- 除了主线程外,可以通过代码来创建其他工作线程。
- 线程是用来执行代码的,每个线程都有一条代码执行路径。
- 线程不是越多越好,每个线程需要栈空间,需要上下文切换,保存上下文状态。
- 多线程优化
2. 概述
C++ 中关于并发多线程的部分,主要包含 <thread>、<mutex>、<atomic>、<condition_varible>、<future>五个部分。
- <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
- <thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
- <mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
- <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
- <future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
3. 创建多线程
- C++ 98标准,需要自己实现跨平台的封装库或者引用第三方库。
- C++ 11 多线程新标准,C++ 语言本身增加对多线程的支持。
- 头文件:<thread>
- 类:thread
- 方法
- get_id
- join
- detach:非常危险的命令,建议不用。特别注意主线程和子线程通过引用,智能指针传递了参数时,主线程退出,会造成变量回收,子线程处于不安全运行;或者通过隐式转换传递参数给子线程时,主线程退出后才执行参数转换,子线程无法正确获取到参数,处于不安全运行。
- joinable
- std::ref:传递对象引用进子线程函数,使主线程和子线程共享。
- std::move:传递智能指针参数到线程函数。
- 用线程方法创建线程:std::thread(thread_work,…)
- 用类创建线程:类必须重载操作符(),void operator()()
- 用类成员函数创建线程:std::thread(&A::thread_work,…)
- 用lambda表达式创建线程
4. 互斥量
- mutex,互斥锁是一个可锁的对象,它被设计成在关键的代码段需要独占访问时发出信号,从而阻止具有相同保护的其他线程并发执行并访问相同的内存位置。
- recursive_mutex,递归互斥锁是一个可锁的对象,就像互斥一样,但是允许同一个线程获得对互斥对象的多个级别的所有权。
- timed_mutex,定时的互斥信号是一个时间可锁定的对象,旨在当关键部分的代码需要独占访问时,就像一个普通互斥,但另外支持定时try-lock请求。
- recursive_timed_mutex,一个递归的互斥对象结合的特点recursive_mutex timed_mutex到单个类的特性:它同时支持由一个线程获取多个锁级别也定时try-lock请求。
- 头文件:<mutex>
- 类:mutex,lock_guard,unique_lock(模板类)
- 方法:lock、unlock
- 死锁:两把或者更多锁,则可能造成死锁。解决死锁方案:1)保证上锁顺序一致,则不会死锁。2)使用std::lock(建议慎用或者不用),std::lock_guard+std::adopt_lock
- unique_lock:可以取代lock_guard,但一般情况下lock_guard足够,unique_lock效率更低,内存占用更多
- std::adopt_lock:互斥量必须先加锁
- std::try_to_lock:互斥量不能先lock
- std::defer_lock :互斥量不能先lock,初始化一个不加锁的互斥量,然后尝试自己lock,unlock,try_lock,relase(与互斥量解绑定,接管后必须手工解锁),但lock后可以不用调用unlock,unlock在析构时会被自动调用
- unique_lock与mutex的所有权可以转让,使用std::move
5. 条件变量
- 头文件:<condition_variable>
- std::condition_variable
- wait
- wait_for
- wait_util
- notify_one
- notify_all
- std::condition_variable_any
- std::notify_all_at_thread_exit
6. 异步任务
- 头文件:<future>
- std::future
- std::shared_future
- std::future_status
- std::async:可以用全局函数,或者类成员函数作参数
- std::packaged_task:包装任务,结合lambda表达式定义函数体,可以将包装对象放进容器
- std::promise