c++ 11 之后有了标准的线程库:std::thread。通过c++11中的线程库创建线程,极为方便,且跨平台,是语言层面的。之前跨平台的多线程开发中,多使用boost 相关第三方库。

1.std::thread 构造函数

默认构造函数       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 <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>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不是一个threadstd::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不是threadt2.join();t4.join();std::cout << "Final value of n is " << n << '\n';
}

2.std::thread 赋值操作

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 <stdio.h>
#include <stdlib.h>#include <chrono>    // std::chrono::seconds
#include <iostream>  // std::cout
#include <thread>    // std::thread, std::this_thread::sleep_forvoid 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;
}

3.其他成员函数

3.1 get_id()

获取线程 ID,返回一个类型为 std::thread::id 的对象。

请看下面例子:

//test.cpp
#include <iostream>
#include <thread>
#include <chrono> //该头文件详情可参考:https://www.cnblogs.com/jwk000/p/3560086.htmlvoid 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

3.2 joinable()

检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程 已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。

#include <iostream>
#include <thread>
#include <chrono>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 exceptionstd::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

3.3 detach()

Detach 线程,将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。

调用 detach 函数之后:

*this 不再代表任何的线程执行实例。
joinable() == false
get_id() == std::thread::id()

另外,如果出错或者 joinable() == false,则会抛出 std::system_error。

#include <iostream>
#include <chrono>
#include <thread>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));
}

3.4 swap()

Swap 线程,交换两个线程对象所代表的底层句柄(underlying handles)。

#include <iostream>
#include <thread>
#include <chrono>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

3.5 native_handle

返回 native handle(由于 std::thread 的实现和操作系统相关,因此该函数返回与 std::thread 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库)。

3.6 hardware_concurrency [static]

检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。

#include <iostream>
#include <thread>int main() {unsigned int n = std::thread::hardware_concurrency();std::cout << n << " concurrent threads are supported.\n";
}

4.std::this_thread 命名空间中相关辅助函数介绍

4.1 get_id()

获取线程 ID。

#include <iostream>
#include <thread>
#include <chrono>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();
}

4.2 yield()

当前线程放弃执行,操作系统调度另一线程继续执行。

#include <iostream>
#include <chrono>
#include <thread>// "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";
}

4.3 sleep_until()

线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。

template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );

4.4 sleep_for()

线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 sleep_duration 所表示的时间片更长。

#include <iostream>
#include <chrono>
#include <thread>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";
}

5.std::thread堆栈使用

#include <iostream>
#include <thread>
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()) 的判断), 原因见上一条。

6.std::thread 线程执行体

std::thread 执行体类似boost::thread, 并不要求是普通的函数,任何可调用的对象都可,具体接受下面四种:

  1. 普通函数
  2. 函数对象
  3. 类成员函数
  4. lambda 函数(匿名函数)
    分别使用举例如下:

(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; };

7.原子变量与线程安全

示例1:

#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();//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 <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 << " us" << endl;return 0;
}
//运行结果:
//num=200000000,用时 40520541 us

通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
互斥量详细内容 请参考C++11 并发之std::mutex。
(2)原子变量。
例如:

#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 << " us" << endl;return 0;
}
//运行结果:
//num=200000000,用时 7111836 us

通过原子变量后运算结果正确,计算速度一般。
(3)通过控制 join位置 。

#include <iostream>
#include <thread>
#include <atomic>
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++实现)

std::thread使用相关推荐

  1. C++11 新特性之std::thread

    C++11 新特性之std::thread 原文:https://blog.csdn.net/oyoung_2012/article/details/78958274 从C++11开始,C++标准库已 ...

  2. std thread

    不要用std::thread写裸线程 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/daan_112211/article/details/1908 ...

  3. 并发,std::thread

    2019独角兽企业重金招聘Python工程师标准>>> std::thread  定义一个线程对象,并管理关联的线程 备注: 你可以使用 thread 对象来观察和管理在应用程序中的 ...

  4. 【多线程】C++11进行多线程开发 (std::thread)

    文章目录 创建线程 std::thread 类 使用join() 使用 detach() 警惕作用域 线程不能复制 给线程传参 传递指针 传递引用 以类成员函数为线程函数 以容器存放线程对象 互斥量 ...

  5. C++11学习笔记-----线程库std::thread

    在以前,要想在C++程序中使用线程,需要调用操作系统提供的线程库,比如linux下的<pthread.h>.但毕竟是底层的C函数库,没有什么抽象封装可言,仅仅透露着一种简单,暴力美 C++ ...

  6. C++ std::thread 和 std::jthread 使用详解 (含C++20新特性)

    目录 std::thread std::thread 构造函数 观察器 操作 std::jthread std::jthread 构造函数 观察器 操作 停止记号处理 管理当前线程的函数 yield( ...

  7. std::jthread与std::thread的区别

    特性上,std::jthread相比std::thread主要增加了以下两个功能: 1.std::jthread对象被destruct时,会自动调用join,等待其所表示的执行流结束. 2.支持外部请 ...

  8. c++11仔细地将参数传递给线程std::thread

    要将参数传递给线程的可关联对象或函数,只需将参数传递给std::thread构造函数. 默认情况下,所有的参数都将复制到新线程的内部存储中. 看一个例子: 给线程传递单个参数 #include < ...

  9. std::thread 不 join

    std::thread 构造之后 使用 detach.就可以了

  10. C++11并发编程:多线程std::thread

    一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语 ...

最新文章

  1. 聊聊hystrix的execution.isolation.semaphore.maxConcurrentRequests属性
  2. 共享数据库、共享数据表
  3. 用js来实现那些数据结构 第一章
  4. 解决由于没有办理权限导致的403,权限不足
  5. Web前端和后端开发的区别和要求
  6. 【华为云技术分享】LiteOS无法直接烧录或者烧录失败解决方法--ST-Link
  7. 玩转 SpringBoot 2 快速整合 Filter 注解版
  8. Dogeswap上线18小时TVL突破300万美金
  9. 火车进出栈问题(高精度+压位+质因数分解消除除法)
  10. 狼性文化遭质疑,那我们当个佛系程序员可好?
  11. 阿里云天池 学习赛汇总(教学赛,零基础入门,长期赛)
  12. 如何用编程方式实现创建一个页面并替换掉站点首页
  13. decimal保留千分位
  14. python做词典_python实现电子词典
  15. 国务院:同意浙江省实施施工图分类审查,低风险项目可不图审!
  16. 搜狗微信添加搜索工具爬虫
  17. 快速入门Flink (7) —— 小白都喜欢看的Flink流处理之DataSources和DataSinks(转载)
  18. android 内部存储 其他,小米MIUI系统怎么清除内部储存空间中“其他”数据?
  19. JVM - 工欲善其事必先利其器之虚拟机工具(下)
  20. 【线上分享】全球多媒体视频内容保护最佳实践

热门文章

  1. JVM:33 如何查看JVM的Full GC日志
  2. 修改 Virtual PC 2007 的显存大小
  3. 非银机构网络支付新规实施 实名制难在保障信息安全
  4. 公司计算机系统忘记用户密码怎么办,win10系统重置修改已经忘记的登录密码的解决步骤...
  5. warning: implicit declaration of function ‘ioctl‘ [-Wimplicit-function-declaration]
  6. Rabin-Karp 指纹字符串查找算法
  7. c语言编写扫雷小游戏下载大全,C语言实现扫雷小游戏
  8. c语言static说明
  9. 梅科尔工作室-王少鹏-鸿蒙笔记2
  10. PostgreSQL 源码解读(154)- 后台进程#6(walsender#2)