C++11之前,window和linux平台分别有各自的多线程标准。使用C++编写的多线程往往是依赖于特定平台的。
在C++11新标准中,正式的为该语言引入了多线程概念。可以简单通过使用thread库,来管理多线程。thread库可以看做对不同平台多线程API的一层包装;
因此使用新标准提供的线程库编写的程序是跨平台的。
其中拷贝构造函数和拷贝赋值运算符被禁用,意味着std::thread对象不能够被拷贝和赋值到别的thread对象;
默认构造函数构造一个空的thread对象,但是不表示任何线程;
接受参数的构造函数创建一个表示线程的对象,线程从传入的函数开始执行,该对象是joinable的;
move构造函数可以看做将一个thread对象对线程的控制权限转移到另一个thread对象;执行之后,传入的thread对象不表示任何线程;
int main()
{
int arg = 0;
std::thread t1; // t1 is not represent a thread
std::thread t2(func1, arg + 1); // pass to thread by value
std::thread t3(func2, std::ref(arg)); // pass to thread by reference
std::thread t4(std::move(t3)); // t4 is now running func2(). t3不再代表任何线程
//t1.join() Error!
t2.join();
//t3.join() Error!
t4.join();
}
对于创建的线程,一般会在其销毁前调用join和detach函数;
弄清楚这两个函数的调用时机和意义,以及调用前后线程状态的变化非常重要。
join实例分析:
int main() {
thread t(tstart, "C++ 11 thread!");
cout << t.joinable() << endl;
if (t.joinable()) t.join();
//t.detach(); Error
cout << t.joinable() << endl;
// t.join(); Error
cout << "Main Function!" << endl;
system("pause");
}
简单来说就是只有处于活动状态的线程才可以调用join,调用返回表示线程执行完毕,状态将变为joinable() == false.
只有joinable() == true的线程,也就是活动状态的线程才可以调用join和detach;所以不可join两次。
当线程既没有调用join也没有调用detach的时候,线程执行完毕状态依然是joinable() == true,那么当thread对象被销毁的时候,会调用terminate()。
线程ID是一个线程的标识符,C++标准中提供两种方式获取线程ID;
有一点需要注意,就是空thread对象,也就是不表示任何线程的thread obj调用get_id返回值为0;
此外当一个线程被detach或者joinable() == false时,调用get_id的返回结果也为0。
除了上面介绍的detach可以分离thread对象及其所表示的线程,或者move到别的线程之外,还可以使用swap来交换两个thread对象表示的线程。
实例来看一下两个线程的交换:
int tstart(const string& tname) {
cout << "Thread test! " << tname << endl;
return 0;
}
int main() {
thread t1(tstart, "C++ 11 thread_1!");
thread t2(tstart, "C++ 11 thread_2!");
cout << "current thread id: " << this_thread::get_id() << endl;
cout << "before swap: "<< " thread_1 id: " << t1.get_id() << " thread_2 id: " << t2.get_id() << endl;
t1.swap(t2);
cout << "after swap: " << " thread_1 id: " << t1.get_id() << " thread_2 id: " << t2.get_id() << endl;
//t.detach();
t1.join();
t2.join();
}
输出:
Thread test! C++ 11 thread_1!
Thread test! C++ 11 thread_2!
current thread id: 39308
before swap: thread_1 id: 26240 thread_2 id: 37276
after swap: thread_1 id: 37276 thread_2 id: 26240
其实交换的过程仅仅是互换了thread对象所持有的底层句柄;
mutex是用来保证线程同步的,防止不同的线程同时操作同一个共享数据。
#include
#include
#include
#include
using namespace std;
mutex m;
int cnt = 10;
void thread1() {
while (cnt > 5){
m.lock();
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
m.unlock();
}
}
void thread2() {
while (cnt > 0) {
m.lock();
if (cnt > 0) {
cnt -= 10;
cout << cnt << endl;
}
m.unlock();
}
}
int main(int argc, char* argv[]) {
thread th1(thread1); //实例化一个线程对象th1,该线程开始执行
thread th2(thread2);
th1.join();
th2.join();
cout << "main..." << endl;
return 0;
}
注意:mutex是不安全的,当一个线程在解锁之前异常退出了,那么其它被阻塞的线程就无法继续下去。
使用lock_guard则相对安全,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构(unlock),不会因为某个线程异常退出而影响其他线程。
#include
#include
#include
using namespace std;
mutex m;
int cnt = 10;
void thread1() {
while (cnt > 5){
lock_guard lockGuard(m);
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
}
}
void thread2() {
while (cnt > 0) {
lock_guard lockGuard(m);
if (cnt > 0) {
cnt -= 10;
cout << cnt << endl;
}
}
}
int main(int argc, char* argv[]) {
thread th1(thread1); //实例化一个线程对象th1,该线程开始执行
thread th2(thread2);
th1.join();
th2.join();
cout << "main..." << endl;
return 0;
}
#include
using namespace std;
void show(const char *str, const int id)
{
cout << "线程 " << id + 1 << " :" << str << endl;
}
int main()
{
thread t1(show, "hello cplusplus!", 0);
thread t2(show, "你好,C++!", 1);
thread t3(show, "hello!", 2);
return 0;
}
#include
using namespace std;
int main()
{
auto fun = [](const char *str) {cout << str << endl; };
thread t1(fun, "hello world!");
thread t2(fun, "hello beijing!");
return 0;
}
输出:
hello world!
hello beijing!
#include
#include
using namespace std;
int show(const char *fun, ...)
{
va_list ap;//指针
va_start(ap, fun);//开始
vprintf(fun, ap);//调用
va_end(ap);
return 0;
}
int main()
{
thread t1(show, "%s %d %c %f", "hello world!", 100, 'A', 3.14159);
return 0;
}
输出:
hello world! 100 A 3.14159