c++ 11 之后有了标准的线程库:std::thread。通过c++11中的线程库创建线程,极为方便,且跨平台,是语言层面的。之前跨平台的多线程开发中,多使用boost 相关第三方库。
默认构造函数 thread() noexcept;
初始化构造函数 template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
拷贝构造函数 [deleted] thread(const thread&) = delete;
Move 构造函数 thread(thread&& x) noexcept;
(1).默认构造函数,创建一个空的 std::thread 执行对象。
(2).初始化构造函数,创建一个 std::thread 对象,该 std::thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 std::thread 对象不可拷贝构造。
(4).Move 构造函数,move 构造函数(move 语义是 C++11 新出现的概念,详见附录),调用成功之后 x 不代表任何 std::thread 执行对象。
注意:可被 joinable 的 std::thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.
std::thread 各种构造函数例子如下:
#include
#include
#include
#include
#include
#include
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << " executing\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
int n = 0;
std::thread t1; // t1 is not a thread,t1不是一个thread
std::thread t2(f1, n + 1); // pass by value,按照值传递
std::thread t3(f2, std::ref(n)); // pass by reference,引用
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread,t4执行t3,t3不是thread
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '\n';
}
Move 赋值操作 thread& operator=(thread&& rhs) noexcept;
拷贝赋值操作 [deleted] thread& operator=(const thread&) = delete;
(1).Move 赋值操作(1),如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则会调用 terminate() 报错。’
(2).拷贝赋值操作(2),被禁用,因此 std::thread 对象不可拷贝赋值。
请看下面的例子:
#include
#include
#include // std::chrono::seconds
#include // std::cout
#include // std::thread, std::this_thread::sleep_for
void thread_task(int n) {
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "hello thread "
<< std::this_thread::get_id()
<< " paused " << n << " seconds" << std::endl;
}
int main(int argc, const char *argv[])
{
std::thread threads[5];
std::cout << "Spawning 5 threads...\n";
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
std::cout << "Done spawning threads! Now wait for them to join\n";
for (auto& t: threads) {
t.join();
}
std::cout << "All threads joined.\n";
return EXIT_SUCCESS;
}
获取线程 ID,返回一个类型为 std::thread::id 的对象。
请看下面例子:
//test.cpp
#include
#include
#include //该头文件详情可参考:https://www.cnblogs.com/jwk000/p/3560086.html
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
t1.join();
t2.join();
return 0;
}
//编译g++ -std=c++11 -pthread test.cpp -o test
从std::thread::id取得int值线程id
std::thread::id tid = std::this_thread::get_id();
_Thrd_t t = *(_Thrd_t*)(char*)&tid ;
unsigned int nId = t._Id
参考资料:https://www.cnblogs.com/yc-only-blog/p/9178935.html
检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程 已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。
#include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t;
std::cout << "before starting, joinable: " << std::boolalpha << t.joinable() << '\n';
t = std::thread(foo);//此时子线程并不执行,在调json()或detach()后才执行
std::cout << "after starting, joinable: " << t.joinable() << '\n';
t.join();//说明:如果此处不调用join(),也不调用detach(),编译执行程序就会提示 terminate called without an active exception
std::cout << "after joining, joinable: " << t.joinable() << '\n';
}
//join: Join 线程,调用该函数会阻塞当前线程,直到由 *this 所标示的线程执行完毕 join 才返回。
std::thread “terminate called without an active exception”请参考如下接:
https://www.cnblogs.com/little-ant/p/3312841.html
Detach 线程,将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。
调用 detach 函数之后:
*this 不再代表任何的线程执行实例。
joinable() == false
get_id() == std::thread::id()
另外,如果出错或者 joinable() == false,则会抛出 std::system_error。
#include
#include
#include
void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.\n";
}
void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::thread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.\n";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
Swap 线程,交换两个线程对象所代表的底层句柄(underlying handles)。
#include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(bar);
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
std::swap(t1, t2);
std::cout << "after std::swap(t1, t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.swap(t2);
std::cout << "after t1.swap(t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.join();
t2.join();
}
执行结果如下:
thread 1 id: 1892
thread 2 id: 2584
after std::swap(t1, t2):
thread 1 id: 2584
thread 2 id: 1892
after t1.swap(t2):
thread 1 id: 1892
thread 2 id: 2584
返回 native handle(由于 std::thread 的实现和操作系统相关,因此该函数返回与 std::thread 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库)。
检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。
#include
#include
int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads are supported.\n";
}
获取线程 ID。
#include
#include
#include
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
t1.join();
t2.join();
}
当前线程放弃执行,操作系统调度另一线程继续执行。
#include
#include
#include
// "busy sleep" while suggesting that other threads run
// for a small amount of time
void little_sleep(std::chrono::microseconds us)
{
auto start = std::chrono::high_resolution_clock::now();
auto end = start + us;
do {
std::this_thread::yield();//让cpu执行其他空闲的线程
} while (std::chrono::high_resolution_clock::now() < end);
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
little_sleep(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;
std::cout << "waited for "
<< std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
<< " microseconds\n";
}
线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 sleep_duration 所表示的时间片更长。
#include
#include
#include
int main()
{
std::cout << "Hello waiter" << std::endl;
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
std::cout << "Waited 2000 ms\n";
}
#include
#include
using namespace std;
void show()
{
cout << "hello cplusplus!" << endl;
}
int main()
{
//栈上
thread t1(show); //根据函数初始化执行
thread t2(show);
thread t3(show);
t1.join();
t2.join();
t3.join();
//线程数组
thread threads[3]{thread(show), thread(show), thread(show)};
for (auto& t: threads)
{
t.join();
}
//堆上
thread *pt1(new thread(show));
thread *pt2(new thread(show));
thread *pt3(new thread(show));
pt1->join();
pt2->join();
pt3->join();
//线程指针数
thread *pthreads(new thread[3]{thread(show), thread(show), thread(show)});
delete pt1;
delete pt2;
delete pt3;
// delete [] pthreads;//这里直接delete会导致异常退出,因为线程没有join(),也没有detach()
int threadNum = 3;
thread* t = new thread[threadNum];
if(t)
{
for ( int i = 0; i < threadNum; i++){
t[i] = thread(show);
}
for ( int i = 0; i < threadNum; i++){
//t[i].detach(); //主进程不等子进程运行完
t[i].join(); //主进程等
}
}
delete [] t;
return 0;
}
注意事项:
1.std::thread在栈内创建后,需要在同一个作用域内调用join() 或者 detach(), 否则退出作用域后,程序会异常退出, 具体原因如下
~thread()
{
if (joinable())
std::terminate();
}
其中的std::terminate()就是用来终止进程的调用。由于std::thread创建后默认是joinable的, 所以需要调用join()或者detach() 来使线程变成非joinable的状态, 具体使用join()还是detach() 取决于实际需求, 如果需要等待线程完成才能继续,那就使用join()来等待, 如果需要立刻返回继续其他操作, 那就调用detach()来脱离对线程的管理, 两者必须选一个。
2.调用new 创建的std::thread, 禁止直接使用delete, 需要在调用delete之前,调用 join()或者 detach() (如果创建线程后立刻调用了这两个函数中任意一个, 可以不再调用, 但是为了保险起见, 需要加上if(joinable()) 的判断), 原因见上一条。
std::thread 执行体类似boost::thread, 并不要求是普通的函数,任何可调用的对象都可,具体接受下面四种:
(1) 普通函数
void hello() //不带参数
{
//do whatever you want
}
void hello(std::string name) //带参数
{
//do whatever you want using parameter in
}
std::thread thread1(hello);
std::thread thread2(hello, "test");
(2) 函数对象
class Hello // 不带参数
{
public: Hello(){}
//over ride operator()
void operator()() const
{
//do whatever you want
}
};
class Hello {
public: Hello() {}
//over ride operator()
void operator()(const std::string name) const
{
// do whatever you want using parameter in
}
};
std::thread thread3(Hello());
std::thread thread4(Hello(), "test");
(3) 类成员函数
class Hello {
public: entity()
{
// do whatever you want
}
//over ride operator()
void entity(const std::string name)
{
// do whatever you want using parameter in name
}
};
Hello obj;
std::thread thread5(&Hello::entity, &obj);
std::thread thread6(&Hello::entity, &obj, "test");
// std::thread thread5(&Hello::entity, this);
// std::thread thread6(&Hello::entity, this, "test");
此种构造也可以借助std::bind使用另外一种方式:
std::thread thread5(std::bind(&Hello::entity, &obj));
std::thread thread6(std::bind(&Hello::entity, &obj, "test"));
(4) lambda 函数
std::thread thread7([](T var1, T var2){
//do whatever you want using parameter var1 var2
}, realValue1, readValue2);
//某匿名函数定义
//auto fun = [](const char *str) {cout << str << endl; };
示例1:
#include
#include
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();//join之后开始执行子线程
t2.join();//join之后开始执行子线程
clock_t end = clock();
cout << "num=" << num << ",用时 " << end - start << " us" << endl;
return 0;
}
//运行结果:
//num=100445432,用时 1013773 us
//注意linux下clock()单位是微妙,windows下clock()单位是毫秒
从上述代码执行的结果,发现结果并不是预计的200000000,这是由于线程之间发生冲突,从而导致结果不正确。为了解决此问题(多线程同时操作同一份数据),有以下方法:
(1)互斥量。
#include
#include
#include
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 << " us" << endl;
return 0;
}
//运行结果:
//num=200000000,用时 40520541 us
通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
互斥量详细内容 请参考C++11 并发之std::mutex。
(2)原子变量。
例如:
#include
#include
#include
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 << " us" << endl;
return 0;
}
//运行结果:
//num=200000000,用时 7111836 us
通过原子变量后运算结果正确,计算速度一般。
(3)通过控制 join位置 。
#include
#include
#include
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 << " us" << endl;
return 0;
}
//运行结果:
//num=200000000,用时 485837 us
运算结果正确,计算速度也很理想。看似没有什么问题,实际上适用场景非常有限,仅适用于一个线程去控制一份数据的情况,大型项目中线程层级关系较多,还是通过数据互斥锁或者设置原子变量的方法来控制
编译 g++ -std=c++11 -pthread test.cpp -o test.exe
参考资料:
http://www.runoob.com/w3cnote/cpp-std-thread.html
https://blog.csdn.net/liuker888/article/details/46848905
https://blog.csdn.net/oyoung_2012/article/details/78958274
https://blog.csdn.net/new_life_sjtu/article/details/52097602?utm_source=blogkpcl2
多线程程序设计之创建线程(Windows下C++实现)