116 lines
4.6 KiB
Markdown
116 lines
4.6 KiB
Markdown
|
# C++11 并发编程
|
|||
|
|
|||
|
C++11 新标准中引入了几个头文件来支持多线程编程,他们分别是:
|
|||
|
|
|||
|
* \<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() 函数就声明在此头文件中。
|
|||
|
|
|||
|
## Thread
|
|||
|
|
|||
|
示例如下:
|
|||
|
|
|||
|
```cpp
|
|||
|
#include <iostream>
|
|||
|
#include <thread>
|
|||
|
|
|||
|
using namespace std;
|
|||
|
|
|||
|
void th_function()
|
|||
|
{
|
|||
|
std::cout << "hello thread." << std::endl;
|
|||
|
}
|
|||
|
|
|||
|
int main(int argc, char *argv[])
|
|||
|
{
|
|||
|
std::thread t(th_function);
|
|||
|
t.join();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
join 是让当前主线程等待所有的子线程执行完,才能退出。
|
|||
|
|
|||
|
线程 detach 脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
|
|||
|
|
|||
|
线程 detach 以后,子线程会成为孤儿线程,线程之间将无法通信。
|
|||
|
|
|||
|
## Mutex
|
|||
|
|
|||
|
Mutex 类
|
|||
|
|
|||
|
* std::mutex,最基本的 Mutex 类,如果当前线程对同一个mutex多次加锁,会产生死锁(dead lock);
|
|||
|
* std::recursive_mutex,递归锁,允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同;
|
|||
|
* std::time_mutex,定时 Mutex 类;
|
|||
|
* std::recursive_timed_mutex,定时递归 Mutex 类。
|
|||
|
|
|||
|
Lock 类
|
|||
|
|
|||
|
* std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁;
|
|||
|
* std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
|
|||
|
|
|||
|
其他类型
|
|||
|
|
|||
|
* std::once_flag,配合std:call_once使用;
|
|||
|
* std::adopt_lock_t,通常作为参数传入给 unique_lock 或 lock_guard 的构造函数;
|
|||
|
* std::defer_lock_t,通常作为参数传入给 unique_lock 的构造函数;
|
|||
|
* std::try_to_lock_t,通常作为参数传入给 unique_lock 的构造函数。
|
|||
|
|
|||
|
函数
|
|||
|
|
|||
|
* std::try_lock,调用时没有获得锁,则直接返回 false;
|
|||
|
* std::lock,调用线程将阻塞等待该互斥量;
|
|||
|
* std::try_lock_for,对定时锁可用,接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时则返回 false;
|
|||
|
* std::try_lock_util,对定时锁可用,接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时则返回 false;
|
|||
|
* std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。
|
|||
|
|
|||
|
## Condition Variable
|
|||
|
|
|||
|
示例如下:
|
|||
|
|
|||
|
```cpp
|
|||
|
#include <iostream> // std::cout
|
|||
|
#include <thread> // std::thread
|
|||
|
#include <mutex> // std::mutex, std::unique_lock
|
|||
|
#include <condition_variable> // std::condition_variable
|
|||
|
|
|||
|
std::mutex mtx; // 全局互斥锁.
|
|||
|
std::condition_variable cv; // 全局条件变量.
|
|||
|
bool ready = false; // 全局标志位.
|
|||
|
|
|||
|
void do_print_id(int id)
|
|||
|
{
|
|||
|
std::unique_lock <std::mutex> lck(mtx);
|
|||
|
while (!ready) // 如果标志位不为 true, 则等待...
|
|||
|
cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
|
|||
|
// 线程被唤醒, 继续往下执行打印线程编号id.
|
|||
|
std::cout << "thread " << id << '\n';
|
|||
|
}
|
|||
|
|
|||
|
void go()
|
|||
|
{
|
|||
|
std::unique_lock <std::mutex> lck(mtx);
|
|||
|
ready = true; // 设置全局标志位为 true.
|
|||
|
cv.notify_all(); // 唤醒所有线程.
|
|||
|
}
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
std::thread threads[10];
|
|||
|
// spawn 10 threads:
|
|||
|
for (int i = 0; i < 10; ++i)
|
|||
|
threads[i] = std::thread(do_print_id, i);
|
|||
|
|
|||
|
std::cout << "10 threads ready to race...\n";
|
|||
|
go(); // go!
|
|||
|
|
|||
|
for (auto & th:threads)
|
|||
|
th.join();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|