C++11 新标准中引入了五个头文件来支持多线程编程:<atomic>,<thread>,<mutex>,<condition_variable> 和 <future>
<atomic>: 该文件主要申明了俩个类,std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类和与C兼容的原子操作的函数。
<thread>: 该文件主要声明了 std::thread 类,另外std::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() 函数就声明在此头文件中。
一. 类std::thread
1.默认构造函数:thread() noexcept;
构造一个任何线程都不执行的线程对象,即空的thread执行对象。
2.初始化函数:
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
构造一个线程对象,可以开启线程执行,该thread对象可被joinable,新执行的线程调用函数fn,并传递args作为参数:
fn
:可以指向函数,指向成员,或是移动构造函数
args
:传递给fn的参数,这些参数可以移动赋值构造。如果fn是一个成员指针,那么第一个args参数就必须是一个对象,或是引用,或是指向该对象的指针。
3.拷贝构造函数 (被禁用):thread (const thread&) = delete
,意味着thread不可被拷贝构造。
4.move构造函数 (移动构造函数):thread (thread && x)noexcept
调用成功后x不代表任何thread执行对象。
std::thread::join
该函数返回时,线程执行完成。
当 一个 thread 调用Join方法的时候,MainThread 就被停止执行,直到该 thread 线程执行完毕。
注意:可被joinable的thread对象必须在他们销毁之前被主线程join或者将其设置为detached。
#include <iostream>
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::cout << "Spawning 3 threads...\n";
std::thread t1 (pause_thread,1);
std::thread t2 (pause_thread,2);
std::thread t3 (pause_thread,3);
std::cout << "Done spawning threads. Now waiting for them to join:\n";
t1.join(); //停下主线程,进入t1 线程
t2.join(); //进入t2线程
t3.join(); // 进入t3线程
std::cout << "All threads joined!\n";
return 0;
}
std::thread::detach
分离线程的对象,使他们彼此独立地执行所表示的线程。这俩个线程既没有被阻止,也没有以任何方式同步。当任一结束执行,其资源被释放。
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::cout << "Spawning and detaching 3 threads...\n";
std::thread (pause_thread,1).detach();
std::thread (pause_thread,2).detach();
std::thread (pause_thread,3).detach();
std::cout << "Done spawning threads.\n";
std::cout << "(the main thread will now pause for 5 seconds)\n";
// give the detached threads time to finish (but not guaranteed!):
pause_thread(5);
return 0;
}
//输出
Output (after 5 seconds):
Spawning and detaching 3 threads...
Done spawning threads.
(the main thread will now pause for 5 seconds)
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 5 seconds ended
更多的thread中的join()和detach()例子。
是否一定要加join()或者detach()呢?其实是不一定的! 看下面程序:
#include<thread>
#include<iostream>
#include <unistd.h>
using namespace std;
class Test{
public:
void run(int num);
};
void Test::run(int num){
int count=100;
while(count>0){
std::cout<<"aaaa: "<<num<<std::endl;
count--;
}
}
class Test2{
private:
Test* testClass;
public:
int printNum(int inputNum){
cout <<"Test2:"<<inputNum<<endl;
}
void setTestClass(Test* testClassInput){
testClass = testClassInput;
}
};
class System{
private:
Test2* pTest2;
Test* pTest;
public:
System(){
//在主线程运行
pTest2 = new Test2();
//开劈新的线程
pTest = new Test();
std::thread* ptTest = new thread(&Test::run, pTest,1);
pTest2->setTestClass(pTest);
}
void printNumViaTest2(int num){
pTest2->printNum(num);
}
};
int main (){
System test;
for(int i=0; i<1000; i++){
test.printNumViaTest2(i);
}
//usleep(1000);
return 0;
}
在上述程序中,没有调用join() 或者 detach() 函数,但是该程序可以正常运行,主要是因为线程在运行的时候,主进程还未退出。有时候,若不用join() 或者 detach() 可能会出现terminate called without an active exception Aborted
的错误,这是因为线程还在运行的时候,主进程就退出了。
初始化 thread 类的构造函数
对于类的成员函数,我们需要给出类对象的地址:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
void fun(int a,int b)
{
std::cout<<"this is a thread"<<std::endl;
}
};
int main()
{
int k=0;
A a;
std::thread t(&A::fun,a,k,k+1);
t.detach();
return 0;
}
std::thread(&A::fun,a,k,k+1); 这个地方就可以看出thread 类的构造对于成员函数的重载了,std::thread t(函数(成员函数)地址,对象地址,成员函数的参数1,参数2,参数3...)。
相比非成员函数,成员函数需要给出类实例化对象的地址,如果该线程是在同一类的某一成员函数当中被构造,则直接用this 关键字代替即可。
二. std::mutex
1.Mutex系列类:
std::mutex,最基本的Mutex类
std::recursive_mutex,递归Mutex类
std::time_mutex,定时Mutex类
std::recursive_timed_mutex,定时递归Mutex类
2.Lock类
std::lock_guard,与Mutex RAII相关,方便线程对互斥量上锁。
std::unique_lock,与Mutex RAII相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
3.函数
std::try_lock,尝试同时对多个互斥量上锁。
std::lock,可以同时对多个互斥量上锁。
std::call_one,如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次。
std::mutex 介绍
std::mutex 是C++11中最基本的互斥量,std::mutex对象提供了独占所有权的特性—即不支持递归地对std::mutex对象上锁,而std::recursize_lock则可以递归地对互斥量对象上锁。
std::mutex 成员函数
1.构造函数,std::mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象是unlocked状态的。
2.lock(), 调用线程将锁住该互斥量。线程调用该函数会发生下面3种情况: (1) 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。(2) 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞。(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁,故lock()后一般需要 unlock()。
3.unlock(), 解锁,释放对互斥量所有权。
4.try_lock(), 尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被堵塞。出现3种情况:(1) 如果当前互斥量没有被其他锁住,则锁住该互斥量。 (2) 如果当前互斥量被其他线程锁住,则当前调用线程返回false,不会堵塞。(3)如果被当前线程锁住了但没有释放,产生死锁。
加锁和解锁问题