转载地址: C++11并发之std::thread_liuker888的博客-CSDN博客
本文概要:
1、成员类型和成员函数。
2、std::thread 构造函数。
3、异步。
4、多线程传递参数。
5、join、detach。
6、获取CPU核心个数。
7、CPP原子变量与线程安全。
8、lambda与多线程。
9、时间等待相关问题。
10、线程功能拓展。
11、多线程可变参数。
12、线程交换。
13、线程移动。
std::thread 在 #include<thread> 头文件中声明,因此使用 std::thread 时需要包含 #include<thread> 头文件。
1、成员类型和成员函数。
成员类型:
Thread id (public member type ) id
Native handle type (public member type )
成员函数:
Construct thread (public member function ) 构造函数
Thread destructor (public member function ) 析构函数
Move-assign thread (public member function ) 赋值重载
Get thread id (public member function ) 获取线程id
Check if joinable (public member function ) 判断线程是否可以加入等待
Join thread (public member function ) 加入等待
Detach thread (public member function ) 分离线程
Swap threads (public member function ) 线程交换
Get native handle (public member function ) 获取线程句柄
Detect hardware concurrency (public static member function ) 检测硬件并发特性
Non-member overloads:
Swap threads (function )
2、std::thread 构造函数。
如下表:
default (1)
thread() noexcept;
initialization(2)
template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)
thread (const thread&) = delete;
move [4]
hread (thread&& x) noexcept;
(1).默认构造函数,创建一个空的 thread 执行对象。
(2).初始化构造函数,创建一个 thread 对象,该 thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4).move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。
std::thread 各种构造函数例子如下:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<chrono>
-
using namespace std;
-
void fun1(int n) //初始化构造函数
-
{
-
cout << "Thread " << n << " executing\n";
-
n += 10;
-
this_thread::sleep_for(chrono::milliseconds(10));
-
}
-
void fun2(int & n) //拷贝构造函数
-
{
-
cout << "Thread " << n << " executing\n";
-
n += 20;
-
this_thread::sleep_for(chrono::milliseconds(10));
-
}
-
int main()
-
{
-
int n = 0;
-
thread t1; //t1不是一个thread
-
thread t2(fun1, n + 1); //按照值传递
-
t2.join();
-
cout << "n=" << n << '\n';
-
n = 10;
-
thread t3(fun2, ref(n)); //引用
-
thread t4(move(t3)); //t4执行t3,t3不是thread
-
t4.join();
-
cout << "n=" << n << '\n';
-
return 0;
-
}
-
运行结果:
-
Thread 1 executing
-
n=0
-
Thread 10 executing
-
n=30</span>
3、异步。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
void show()
-
{
-
cout << "hello cplusplus!" << endl;
-
}
-
int main()
-
{
-
//栈上
-
thread t1(show); //根据函数初始化执行
-
thread t2(show);
-
thread t3(show);
-
//线程数组
-
thread th[3]{thread(show), thread(show), thread(show)};
-
//堆上
-
thread *pt1(new thread(show));
-
thread *pt2(new thread(show));
-
thread *pt3(new thread(show));
-
//线程指针数组
-
thread *pth(new thread[3]{thread(show), thread(show), thread(show)});
-
return 0;
-
}</span>
4、多线程传递参数。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
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;
-
}
-
运行结果:
-
线程 1线程 2 :你好,C++!线程 3 :hello!
-
:hello cplusplus!</span>
发现,线程 t1、t2、t3 都执行成功!
5、join、detach。
join例子如下:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<array>
-
using namespace std;
-
void show()
-
{
-
cout << "hello cplusplus!" << endl;
-
}
-
int main()
-
{
-
array<thread, 3> threads = { thread(show), thread(show), thread(show) };
-
for (int i = 0; i < 3; i++)
-
{
-
cout << threads[i].joinable() << endl;//判断线程是否可以join
-
threads[i].join();//主线程等待当前线程执行完成再退出
-
}
-
return 0;
-
}
-
运行结果:
-
hello cplusplus!
-
hello cplusplus!
-
1
-
hello cplusplus!
-
1
-
1</span>
总结:
join 是让当前主线程等待所有的子线程执行完,才能退出。
detach例子如下:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
void show()
-
{
-
cout << "hello cplusplus!" << endl;
-
}
-
int main()
-
{
-
thread th(show);
-
//th.join();
-
th.detach();//脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
-
//detach以后,子线程会成为孤儿线程,线程之间将无法通信。
-
cout << th.joinable() << endl;
-
return 0;
-
}
-
运行结果:
-
hello cplusplus!
-
0</span>
结论:
线程 detach 脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
线程 detach以后,子线程会成为孤儿线程,线程之间将无法通信。
6、获取CPU核心个数。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
int main()
-
{
-
auto n = thread::hardware_concurrency();//获取cpu核心个数
-
cout << n << endl;
-
return 0;
-
}
-
运行结果:
-
8</span>
结论:
通过 thread::hardware_concurrency() 获取 CPU 核心的个数。
7、CPP原子变量与线程安全。
问题例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
const int N = 100000000;
-
int num = 0;
-
void run()
-
{
-
for (int i = 0; i < N; i++)
-
{
-
num++;
-
}
-
}
-
int main()
-
{
-
clock_t start = clock();
-
thread t1(run);
-
thread t2(run);
-
t1.join();
-
t2.join();
-
clock_t end = clock();
-
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
-
return 0;
-
}
-
运行结果:
-
num=143653419,用时 730 ms</span>
从上述代码执行的结果,发现结果并不是我们预计的200000000,这是由于线程之间发生冲突,从而导致结果不正确。
为了解决此问题,有以下方法:
(1)互斥量。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<mutex>
-
using namespace std;
-
const int N = 100000000;
-
int num(0);
-
mutex m;
-
void run()
-
{
-
for (int i = 0; i < N; i++)
-
{
-
m.lock();
-
num++;
-
m.unlock();
-
}
-
}
-
int main()
-
{
-
clock_t start = clock();
-
thread t1(run);
-
thread t2(run);
-
t1.join();
-
t2.join();
-
clock_t end = clock();
-
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
-
return 0;
-
}
-
运行结果:
-
num=200000000,用时 128323 ms</span>
不难发现,通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
互斥量详细内容 请参考C++11 并发之std::mutex。
(2)原子变量。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<atomic>
-
using namespace std;
-
const int N = 100000000;
-
atomic_int num{ 0 };//不会发生线程冲突,线程安全
-
void run()
-
{
-
for (int i = 0; i < N; i++)
-
{
-
num++;
-
}
-
}
-
int main()
-
{
-
clock_t start = clock();
-
thread t1(run);
-
thread t2(run);
-
t1.join();
-
t2.join();
-
clock_t end = clock();
-
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
-
return 0;
-
}
-
运行结果:
-
num=200000000,用时 29732 ms</span>
不难发现,通过原子变量后运算结果正确,计算速度一般。
原子变量详细内容 请参考C++11 并发之std::atomic。
(3)加入 join 。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
const int N = 100000000;
-
int num = 0;
-
void run()
-
{
-
for (int i = 0; i < N; i++)
-
{
-
num++;
-
}
-
}
-
int main()
-
{
-
clock_t start = clock();
-
thread t1(run);
-
t1.join();
-
thread t2(run);
-
t2.join();
-
clock_t end = clock();
-
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
-
return 0;
-
}
-
运行结果:
-
num=200000000,用时 626 ms</span>
不难发现,通过原子变量后运算结果正确,计算速度也很理想。
8、lambda与多线程。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
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!</span>
9、时间等待相关问题。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<chrono>
-
using namespace std;
-
int main()
-
{
-
thread th1([]()
-
{
-
//让线程等待3秒
-
this_thread::sleep_for(chrono::seconds(3));
-
//让cpu执行其他空闲的线程
-
this_thread::yield();
-
//线程id
-
cout << this_thread::get_id() << endl;
-
});
-
return 0;
-
}</span>
10、线程功能拓展。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
class MyThread :public thread //继承thread
-
{
-
public:
-
//子类MyThread()继承thread()的构造函数
-
MyThread() : thread()
-
{
-
}
-
//MyThread()初始化构造函数
-
template<typename T, typename...Args>
-
MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)
-
{
-
}
-
void showcmd(const char *str) //运行system
-
{
-
system(str);
-
}
-
};
-
int main()
-
{
-
MyThread th1([]()
-
{
-
cout << "hello" << endl;
-
});
-
th1.showcmd("calc"); //运行calc
-
//lambda
-
MyThread th2([](const char * str)
-
{
-
cout << "hello" << str << endl;
-
}, " this is MyThread");
-
th2.showcmd("notepad");//运行notepad
-
return 0;
-
}
-
运行结果:
-
hello
-
//运行calc
-
hello this is MyThread
-
//运行notepad</span>
11、多线程可变参数。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
#include<cstdarg>
-
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</span>
12、线程交换。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
int main()
-
{
-
thread t1([]()
-
{
-
cout << "thread1" << endl;
-
});
-
thread t2([]()
-
{
-
cout << "thread2" << endl;
-
});
-
cout << "thread1' id is " << t1.get_id() << endl;
-
cout << "thread2' id is " << t2.get_id() << endl;
-
cout << "swap after:" << endl;
-
swap(t1, t2);//线程交换
-
cout << "thread1' id is " << t1.get_id() << endl;
-
cout << "thread2' id is " << t2.get_id() << endl;
-
return 0;
-
}
-
运行结果:
-
thread1
-
thread2
-
thread1' id is 4836
-
thread2' id is 4724
-
swap after:
-
thread1' id is 4724
-
thread2' id is 4836</span>
两个线程通过 swap 进行交换。
13、线程移动。
例如:
-
<span style="font-size:12px;">#include<iostream>
-
#include<thread>
-
using namespace std;
-
int main()
-
{
-
thread t1([]()
-
{
-
cout << "thread1" << endl;
-
});
-
cout << "thread1' id is " << t1.get_id() << endl;
-
thread t2 = move(t1);;
-
cout << "thread2' id is " << t2.get_id() << endl;
-
return 0;
-
}
-
运行结果:
-
thread1
-
thread1' id is 5620
-
thread2' id is 5620</span>
从上述代码中,线程t2可以通过 move 移动 t1 来获取 t1 的全部属性,而 t1 却销毁了。