C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是:<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。
创建线程
<thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
线程间同步
<atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
<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() 函数就声明在此头文件中。
std::thread
thread构造函数:创建一个线程。
joinable()判断线程线程对象是否可连接,换句话说就是判断该线程是否变成了孤儿进程,如果为可连接则返回1。
join()函数被调用后,主线程阻塞,直到子线程(也就是join所属的thread构造函数创建的线程)执行完,当前线程才会重新开始执行。join还用于销毁子进程。
detach()子线程调用后,子线程和父线程会分离,分离后,父线程不会由于调用join而阻塞。此时由thread创建的线程变成守护线程,由INIT线程接管。其内存空间在它终止时由系统自动释放。
比较linux中的API
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
int pthread_join(pthread_t thread, void **retval);
int pthread_detach(pthread_t thread);
pthread_self());
future
为什么C++11引入std::future和std::promise?
future::get封装了mutex 和conditional_wait
future::set封装了 mutex 和conditional_wait,并对共享变量赋值,从而简化了线程间同步编程
<future> 头文件中包含了以下几个类和函数:
Providers 类:std::promise, std::package_task
Futures 类:std::future, shared_future.
Providers 函数:std::async()
其他类型:std::future_error, std::future_errc, std::future_status, std::launch.
promise::get_future()用于返回一个future对象
future::set用于获取锁,初始化future->a的值,唤醒阻塞在a上的线程,然后释放锁
future::get用于获取锁,阻塞,等待至a的值被初始化,再开始执行
packaged_task<T>则是对std::promise<function<tp>>的封装,也就是说T为函数指针类型
此时执行future::set 共享变量会被初始化为一个函数指针。
future和shared_future的区别
可以处理所有在线程间数据转移的必要同步,但是std::future模型独享同步结果的所有权。并且通过 get() 函数,一次性的获取数据,让并发访问变的毫无意义。你的并发代码没有办法让多个线程等待同一个事件。
std::shared_future 可以完成让多个线程的等待
std::async
template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);
对thread和packaged_task的封装:其创建线程,并获取锁,对共享变量future进行操作,并唤醒阻塞进程,释放锁
std::future<T> res= std::async();//返回future类型结构体
res.get(); //进程阻塞、释放锁,等待数据到来
线程间通信
误区:线程间通信只能采用全局变量的方法;
实际上,同一进程内的线程可以访问任何线程的任何变量,只要该线程有那个变量的地址即可。一般可以通过形参传递该变量的地址。
其实多线程通信采用全局变量会增加耦合性。修改全部变量会影响所有用到它的模块,不利于调试;影响函数的封装性能:我们肯定是希望我们写的函数具有重入性。 线程不安全,多线程中多全局变量的修改容易冲突,需要加锁。
所以线程间通信尽量少采用全局变量的方式,而是采用形参传递,<future>内即采用的该方法。