在C++ 11之前,官方并没有支持线程库。C++ 11通过标准库引入了对 thread 类的支持,大大方便了完成多线程开发的工作。

std::thread 构造函数 

(1)thread() noexcept;

(2)thread( thread&& other ) noexcept;

(3)template< class Function, class... Args >

explicit thread( Function&& f, Args&&... args );

(4)thread(const thread&) = delete;

(1) 构造新的 thread 对象,但由于没有传入函数,所以thread对象还没有关联到线程。

(2) 移动构造函数。构造表示曾为 other 所表示的执行线程的 thread 对象。此调用后 other 不再表示执行线程。

(3) 构造新的 std::thread 对象并将它与执行线程关联。新的执行线程开始执行。

(4) 复制构造函数被删除, thread 不可复制。

下面我们来看一段代码:

#include <iostream>

#include <utility>

#include <thread>

#include <chrono>

void f1(int n)

{

for (int i = 0; i < 5; ++i) {

std::cout << "Thread 1 executing\n";

++n;

std::this_thread::sleep_for(std::chrono::milliseconds(10));  //毫秒级

   // std::this_thread::sleep_for(std::chrono::seconds(1));  //秒级

}

}

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));

}

}

class foo

{

public:

void bar()

{

for (int i = 0; i < 5; ++i) {

std::cout << "Thread 3 executing\n";

++n;

std::this_thread::sleep_for(std::chrono::milliseconds(10));

}

}

int n = 0;

};

class baz

{

public:

void operator()()

{

for (int i = 0; i < 5; ++i) {

std::cout << "Thread 4 executing\n";

++n;

std::this_thread::sleep_for(std::chrono::milliseconds(10));

}

}

int n = 0;

};

int main()

{

int n = 0;

foo f;

baz b;

std::thread t1; // t1 不是线程

std::thread t2(f1, n + 1); // 按值传递

std::thread t3(f2, std::ref(n)); // 按引用传递

std::thread t4(std::move(t3)); // t4 现在运行 f2() 。 t3 不再是线程

std::thread t5(&foo::bar, &f); // t5 在对象 f 上运行 foo::bar()

std::thread t6(std::ref(b)); // t6 在对象 b 上运行 baz::operator()

t2.join();

t4.join();

t5.join();

t6.join();

std::cout << "Final value of n is " << n << '\n';

std::cout << "Final value of foo::n is " << f.n << '\n';

std::cout << "Final value of baz::n is " << b.n << '\n';

}

注意:若需要传递引用参数给线程函数,则必须包装它 (例如用 std::ref 或 std::cref)。忽略来自函数的任何返回值。若函数抛异常,则调用 std::exception()。

观察器

joinable

bool joinable() const noexcept;

用于判断 thread 对象是否关联到某一线程,若 thread 对象与执行线程关联,则返回 true ,反之为 false 。

#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);

std::cout << "after starting, joinable: " << t.joinable()

<< '\n';

t.join();

std::cout << "after joining, joinable: " << t.joinable()

<< '\n';

}

输出信息:

before starting, joinable: false

after starting, joinable: true

after joining, joinable: false

操作

join

void join();

阻塞当前线程直至 *this 所标识的线程结束其执行。*this 所标识的线程的完成同步于对应的从 join() 成功返回。*this 自身上不进行同步。同时从多个线程在同一 thread 对象上调用 join() 构成数据竞争,导致未定义行为。

#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::cout << "starting first helper...\n";

std::thread helper1(foo);

std::cout << "starting second helper...\n";

std::thread helper2(bar);

std::cout << "waiting for helpers to finish..." << std::endl;

helper1.join();

helper2.join();

std::cout << "done!\n";

}

输出信息:

starting first helper...

starting second helper...

waiting for helpers to finish...

done!

get_id

std::thread::id get_id() const noexcept;

返回标识与 *this 关联的线程的 std::thread::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();

}

t1's id: 0x35a7210f

t2's id: 0x35a311c4

detach

void detach();

从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。调用 detach 后 *this 不再占有任何线程。

#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));

}

输出信息:

Starting thread caller.

Starting concurrent thread.

Exiting thread caller.

Exiting concurrent thread.

swap

void swap( std::thread& other ) noexcept;

交换二个 thread 对象的底层柄。

#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() << '\n'

<< "thread 2 id: " << t2.get_id() << '\n';

std::swap(t1, t2);

std::cout << "after std::swap(t1, t2):" << '\n'

<< "thread 1 id: " << t1.get_id() << '\n'

<< "thread 2 id: " << t2.get_id() << '\n';

t1.swap(t2);

std::cout << "after t1.swap(t2):" << '\n'

<< "thread 1 id: " << t1.get_id() << '\n'

<< "thread 2 id: " << t2.get_id() << '\n';

t1.join();

t2.join();

}

输出信息:

thread 1 id: 140185268262656

thread 2 id: 140185259869952

after std::swap(t1, t2):

thread 1 id: 140185259869952

thread 2 id: 140185268262656

after t1.swap(t2):

thread 1 id: 140185268262656

thread 2 id: 140185259869952

总结:

(0x01) std::thread 类创建线程非常方便,构造 thread 对象时传入一个需要运行的函数及其参数。构造完成后,新的线程马上被创建,同时执行该对象。注意:若需要传递引用参数给线程函数,则必须包装它(例如用 std::ref 或 std::cref)。

(0x02) 使用 std::thread 默认的构造函数构造对象时,该对象是不关联任何线程的。可以在之后的使用过程中再关联到某一线程。可以通过使用 joinable() 接口,判断一个 thread 对象是否关联某个线程。

(0x03) 关联到线程的 thread 对象析构前,必须调用 join() 接口等待线程结束。或者 thread 对象调用 detach() 接口解除与线程的关联,否则会抛异常。

(0x04) thread 对象 detach() 后会独立执行直至结束,而对应的 thread 对象变成不关联任何线程的对象,joinable() 将返回 false。

(0x05) std::thread 没有拷贝构造函数和拷贝赋值操作符,因此不支持复制操作(但从构造函数的示例代码可以看出 std::thread 可以 move )。这也说明了没有两个 thread 对象可以表示同一执行线程。

C++ 多线程 std::thread 使用总结的更多相关文章

  1. C++11多线程std::thread的简单使用

    在cocos2dx 2.0时代,我们使用的是pthread库,是一套用户级线程库,被广泛地使用在跨平台应用上.但在cocos2dx 3.0中并未发现有pthread的支持文件,原来c++11中已经拥有 ...

  2. C++11并发——多线程std::thread (一)

    https://www.cnblogs.com/haippy/p/3284540.html 与 C++11 多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< ...

  3. Cocos2dx 3.0 过渡篇(二十七)C++11多线程std::thread的简单使用(下)

    本篇接上篇继续讲:上篇传送门:http://blog.csdn.net/star530/article/details/24186783 简单的东西我都说的几乎相同了,想挖点深的差点把自己给填进去. ...

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

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

  5. Cocos2dx 3.0 过渡篇(二十六)C++11多线程std::thread的简单使用(上)

    昨天练车时有一MM与我交替着练,聊了几句话就多了起来,我对她说:"看到前面那俩教练没?老色鬼两枚!整天调戏女学员."她说:"还好啦,这毕竟是他们的乐趣所在,你不认为教练每 ...

  6. C++11多线程std::thread创建方式

    //#include <cstdlib> //#include <cstdio> //#include <cstring> #include <string& ...

  7. C++11 多线程编程 使用lambda创建std::thread (生产/消费者模式)

    要写个tcp server / client的博客,想着先写个c++11多线程程序.方便后面写博客使用. 目前c++11中写多线程已经很方便了,不用再像之前的pthread_create,c++11中 ...

  8. c++ 如何获取多线程的返回值?(std::thread ,std::async)

    //简单的 c++11 线程,简单方便,成员函数随便调用,非成员函数也一样,如需要获取返回时,请自行使用条件变量 std::thread run([&](){ //执行一些耗时的操作 retu ...

  9. c++11多线程记录1 -- std::thread

    启动一个线程 话不多说,直接上代码 void func(); int main() { std::thread t(func); //这里就开始启动线程了 t.join(); // 必须调用join或 ...

随机推荐

  1. [LeetCode]面试题14- I. 剪绳子(DP/贪心)

    题目 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m.n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m] .请问 k[0]k[1]...* ...

  2. [补题]匹配%#,%#之间的字符串重复%前的num遍

    题目 匹配%#,%#之间的字符串重复%前的num遍. 样例1: 3%acm#2%acm# 输出: acmacmacmacmacm 样例2: 3%2%acm## 输出: acmacmacmacmacm ...

  3. nginx.config 多个方案解决跨域问题

    #user nobody; user sam owner; worker_processes 1; #error_log logs/error.log; #error_log logs/error.l ...

  4. (数据科学学习手札95)elyra——jupyter lab平台最强插件集

    本文示例文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 jupyter lab是我最喜欢的编辑器,在过往 ...

  5. RXJAVA之异步操作

    Observable提供了一些do方法来快速提供监听响应事件. doOnComplete 当complete时,执行action. doOnTerminate 当结束执行action,无论是正常还是异 ...

  6. Java基础一篇过(三)I/O流总结

    一.啥是I/O 概念:I/O为输入,输出流的统称,流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象. 本质:本质是数据传输,即数据在两设备间的传输称为流,根据数据传输特性将流抽象为各 ...

  7. 【测试基础第六篇】bug定义及生命周期

    bug定义 狭义:软件程序的漏洞或缺陷 广义:测试工程师或用户所发现和提出的软件可改进的细节(增强型.建议性)或需求文档存在差异的功能实现 职责:发现bug,提给开发,让其修改 bug类型--了解 代 ...

  8. 什么是垃圾搜集(GC)?为什么要有GC呢?

    GC的全称是Gabage Collection,翻译过来就是"垃圾收集"的意思.那么我们为什么用GC呢? 那么我们接下来就来聊一聊GC的创造背景.在C和C++那个年代的程序员界的长 ...

  9. 基础篇:深入解析JAVA异常

    目录 1 异常的分类和继承关系 2 几种常见异常类的解析 3 Java异常关键字 4 开发过程处理异常注意点 5 异常和AutoCloseable(1.7-JDK的语法糖) 6 throw和throw ...

  10. Centos-删除文件或目录-rm

    rm 删除目录或者文件,如果是链接文件,则只删除这个链接文件而不是链接指向的文件 相关选项 -r 递归删除目录 -f 忽略不存在提示和确认提示,本身确认提示系统默认添加-i参数 -i 删除文件前提示, ...