注:此教程以 Visual Studio 2019 Version 16.10.3 (MSVC 19.29.30038.1) 为标准,大多数内容参照cplusplus.com里的解释
此文章允许转载,但请标明出处(https://blog.csdn.net/sjc_0910/article/details/118861539)

前方高能:本文字数接近2万

文章目录

  • 线程?进程?多线程?
    • 什么是多线程?
    • 进程与线程的区别
  • C++11的std::thread
    • std::thread常用成员函数
      • 构造&析构函数
      • 常用成员函数
    • 举个栗子
      • 例一:thread的基本使用
      • 例二:thread执行有参数的函数
      • 例三:thread执行带有引用参数的函数
    • 注意事项
  • C++11中的std::atomic和std::mutex
    • 为什么要有atomic和mutex
    • std::mutex
      • 例四:std::mutex的使用
      • mutex的常用成员函数
    • std::atomic
      • 例五:std::atomic的使用
        • 代码解释
      • std::atomic常用成员函数
        • 构造函数
        • 常用成员函数
  • C++11中的std::async
    • 为什么大多数情况下使用async而不用thread
    • std::async参数
      • std::launch强枚举类(enum class)
    • 例六:std::async的使用
  • C++11中的std::future
    • 例七:使用std::future获取线程的返回值
      • 代码解释
    • std::future常用成员函数
      • 构造&析构函数
      • 常用成员函数
    • std::future_status强枚举类
    • 为啥要有void特化的std::future?
      • 例八:void特化std::future
  • C++11中的std::promise
    • 例九:引用传递返回值
    • std::promise到底是啥
      • 例十:std::future的值不能改变,那么如何利用引用传递返回值
    • std::promise常用成员函数
      • 构造&析构函数
      • 常用成员函数
    • 例十一:std::promise的使用
  • C++11中的std::this_thread
    • std::this_thread常用函数
    • 例十二:std::this_thread中常用函数的使用
  • 结尾

线程?进程?多线程?

什么是多线程?

百度百科中的解释:

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。

进程与线程的区别

定义:

进程是正在运行的程序的实例,而线程是是进程中的实际运作单位。

区别:

  • 一个程序有且只有一个进程,但可以拥有至少一个的线程。
  • 不同进程拥有不同的地址空间,互不相关,而不同线程共同拥有相同进程的地址空间。

看了上述介绍,你应该明白进程与线程的区别了。什么,还不明白?下面这幅图应该能让你搞清楚:

(自己画的图,不好看请见谅)

C++11的std::thread

在C中已经有一个叫做pthread的东西来进行多线程编程,但是并不好用 (如果你认为句柄、回调式编程很实用,那请当我没说),所以c++11标准库中出现了一个叫作std::thread的东西。

std::thread常用成员函数

构造&析构函数

函数 类别 作用
thread() noexcept 默认构造函数 创建一个线程,
什么也不做
template <class Fn, class… Args>
explicit thread(Fn&& fn, Args&&… args)
初始化构造函数 创建一个线程,
args为参数
执行fn函数
thread(const thread&) = delete 复制构造函数 (已删除)
thread(thread&& x) noexcept 移动构造函数 构造一个与x
相同的对象,会破坏x对象
~thread() 析构函数 析构对象

常用成员函数

函数 作用
void join() 等待线程结束并清理资源(会阻塞)
bool joinable() 返回线程是否可以执行join函数
void detach() 将线程与调用其的线程分离,彼此独立执行(此函数必须在线程创建时立即调用,且调用此函数会使其不能被join)
std::thread::id get_id() 获取线程id
thread& operator=(thread &&rhs) 见移动构造函数
(如果对象是joinable的,那么会调用std::terminate()结果程序)

举个栗子

例一:thread的基本使用

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
void doit() { cout << "World!" << endl; }
int main() {// 这里的线程a使用了 C++11标准新增的lambda函数// 有关lambda的语法,请参考我之前的一篇博客// https://blog.csdn.net/sjc_0910/article/details/109230162thread a([]{cout << "Hello, " << flush;}), b(doit);a.join();b.join();return 0;
}

输出结果:

Hello, World!

或者是

World!
Hello,

那么,为什么会有不同的结果呢?
这就是多线程的特色!

多线程运行时是以异步方式执行的,与我们平时写的同步方式不同。异步方式可以同时执行多条语句。

在上面的例子中,我们定义了2个thread,这2个thread在执行时并不会按照一定的顺序。打个比方,2个thread执行时,就好比赛跑,谁先跑到终点,谁就先执行完毕。

例二:thread执行有参数的函数

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
void countnumber(int id, unsigned int n) {for (unsigned int i = 1; i <= n; i++);cout << "Thread " << id << " finished!" << endl;
}
int main() {thread th[10];for (int i = 0; i < 10; i++)th[i] = thread(countnumber, i, 100000000);for (int i = 0; i < 10; i++)th[i].join();return 0;
}

你的输出有可能是这样

Thread 2 finished!Thread 3 finished!
Thread 7 finished!
Thread 5 finished!Thread 8 finished!
Thread 4 finished!
Thread 6 finished!
Thread 0 finished!
Thread 1 finished!
Thread 9 finished!

注意:我说的是有可能。你的运行结果可能和我的不一样,这是正常现象,在上一个例子中我们分析过原因。

这个例子中我们在创建线程时向函数传递了一些参数,但如果要传递引用参数呢?是不是像这个例子中直接传递就行了?让我们来看看第三个例子:

例三:thread执行带有引用参数的函数

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
template<class T> void changevalue(T &x, T val) {x = val;
}
int main() {thread th[100];int nums[100];for (int i = 0; i < 100; i++)th[i] = thread(changevalue<int>, nums[i], i+1);for (int i = 0; i < 100; i++) {th[i].join();cout << nums[i] << endl;}return 0;
}

如果你尝试编译这个程序,那你的编译器一定会报错

E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(55): error C2672: “std::invoke”: 未找到匹配的重载函数
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(61): note: 查看对正在编
译的函数 模板 实例化“unsigned int std::thread::_Invoke<_Tuple,0,1,2>(void *) noexcept”的引用with[_Tuple=_Tuple]
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(66): note: 查看对正在编
译的函数 模板 实例化“unsigned int (__cdecl *std::thread::_Get_invoke<_Tuple,0,1,2>(std::integer_sequence<size_t,0,1,2>) noexcept)(void *) noexcept”的引用
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(89): note: 查看对正在编
译的函数 模板 实例化“void std::thread::_Start<void(__cdecl &)(T &,T),int&,_Ty>(_Fn,int &,_Ty &&)”的引用with[T=int,_Ty=int,_Fn=void (__cdecl &)(int &,int)]
main.cpp(11): note: 查看对正在编译的函数 模板 实例化“std::thread::thread<void(__cdecl &)(T &,T),int&,int,0>(_Fn,int &,int &&)”
的引用with[T=int,_Fn=void (__cdecl &)(int &,int)]
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): error C2893: 未能使
函数模板“unknown-type std::invoke(_Callable &&,_Ty1 &&,_Types2 &&...) noexcept(<expr>)”专用化
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\type_traits(1589): note: 参见“std::invoke”的声明
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): note: 用下列模板参
数:
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): note: “_Callable=void (__cdecl *)(T &,T)”
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): note: “_Ty1=int”
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): note: “_Types2={int}”
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\thread(51): error C2780: “unknown-type std::invoke(_Callable &&) noexcept(<expr>)”: 应输入 1 个参数,却提供了 3 个
E:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\type_traits(1583): note: 参见“std::invoke”的声明

这是怎么回事呢?原来thread在传递参数时,是以右值传递的:

template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args)

划重点:Args&&... args
很明显的右值引用,那么我们该如何传递一个左值呢?std::refstd::cref很好地解决了这个问题。
std::ref 可以包装按引用传递的值。
std::cref 可以包装按const引用传递的值。
针对上面的例子,我们可以使用以下代码来修改:

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
template<class T> void changevalue(T &x, T val) {x = val;
}
int main() {thread th[100];int nums[100];for (int i = 0; i < 100; i++)th[i] = thread(changevalue<int>, ref(nums[i]), i+1);for (int i = 0; i < 100; i++) {th[i].join();cout << nums[i] << endl;}return 0;
}

这次编译可以成功通过,你的程序输出的结果应该是这样的:

1
2
3
4
...
99
100

(中间省略了一堆数)

注意事项

  • 线程是在thread对象被定义的时候开始执行的,而不是在调用join函数时才执行的,调用join函数只是阻塞等待线程结束并回收资源。
  • 分离的线程(执行过detach的线程)会在调用它的线程结束或自己结束时释放资源。
  • 线程会在函数运行完毕后自动释放,不推荐利用其他方法强制结束线程,可能会因资源未释放而导致内存泄漏。
  • 没有执行joindetach的线程在程序结束时会引发异常

C++11中的std::atomic和std::mutex

我们现在已经知道如何在c++11中创建线程,那么如果多个线程需要操作同一个变量呢?

为什么要有atomic和mutex

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
int n = 0;
void count10000() {for (int i = 1; i <= 10000; i++)n++;
}
int main() {thread th[100];// 这里偷了一下懒,用了c++11的foreach结构for (thread &x : th)x = thread(count10000);for (thread &x : th)x.join();cout << n << endl;return 0;
}

我的2次输出结果分别是:

991164
996417

我们的输出结果应该是1000000,可是为什么实际输出结果比1000000小呢?
在上文我们分析过多线程的执行顺序——同时进行、无次序,所以这样就会导致一个问题:多个线程进行时,如果它们同时操作同一个变量,那么肯定会出错。为了应对这种情况,c++11中出现了std::atomicstd::mutex

std::mutex

std::mutex是 C++11 中最基本的互斥量,一个线程将mutex锁住时,其它的线程就不能操作mutex,直到这个线程将mutex解锁。根据这个特性,我们可以修改一下上一个例子中的代码:

例四:std::mutex的使用

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int n = 0;
mutex mtx;
void count10000() {for (int i = 1; i <= 10000; i++) {mtx.lock();n++;mtx.unlock();}
}
int main() {thread th[100];for (thread &x : th)x = thread(count10000);for (thread &x : th)x.join();cout << n << endl;return 0;
}

执行了好几次,输出结果都是1000000,说明正确。

mutex的常用成员函数

(这里用mutex代指对象

函数 作用
void lock() 将mutex上锁。
如果mutex已经被其它线程上锁,
那么会阻塞,直到解锁;
如果mutex已经被同一个线程锁住,
那么会产生死锁。
void unlock() 解锁mutex,释放其所有权。
如果有线程因为调用lock()不能上锁而被阻塞,则调用此函数会将mutex的主动权随机交给其中一个线程;
如果mutex不是被此线程上锁,那么会引发未定义的异常。
bool try_lock() 尝试将mutex上锁。
如果mutex未被上锁,则将其上锁并返回true;
如果mutex已被锁则返回false。

std::atomic

mutex很好地解决了多线程资源争抢的问题,但它也有缺点:太……慢……了……
以例四为标准,我们定义了100个thread,每个thread要循环10000次,每次循环都要加锁、解锁,这样固然会浪费很多的时间,那么该怎么办呢?接下来就是atomic大展拳脚的时间了。

例五:std::atomic的使用

根据atomic的定义,我又修改了例四的代码:

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
// #include <mutex> //这个例子不需要mutex了
#include <atomic>
using namespace std;
atomic_int n = 0;
void count10000() {for (int i = 1; i <= 10000; i++) {n++;}
}
int main() {thread th[100];for (thread &x : th)x = thread(count10000);for (thread &x : th)x.join();cout << n << endl;return 0;
}

输出结果:1000000,正常

代码解释

可以看到,我们只是改动了n的类型(int->std::atomic_int),其他的地方一点没动,输出却正常了。
有人可能会问了:这个std::atomic_int是个什么玩意儿?其实,std::atomic_int只是std::atomic<int>的别名罢了。
atomic,本意为原子,官方 (我不确定是不是官方,反正继续解释就对了) 对其的解释是

原子操作是最小的且不可并行化的操作。

这就意味着即使是多线程,也要像同步进行一样同步操作atomic对象,从而省去了mutex上锁、解锁的时间消耗。

std::atomic常用成员函数

构造函数

对,atomic没有显式定义析构函数

函数 类型 作用
atomic() noexcept = default 默认构造函数 构造一个atomic对象(未初始化,可通过atomic_init进行初始化)
constexpr atomic(T val) noexcept 初始化构造函数 构造一个atomic对象,用val的值来初始化
atomic(const atomic&) = delete 复制构造函数 (已删除)

常用成员函数

atomic能够直接当作普通变量使用,成员函数貌似没啥用,所以这里就不列举了,想搞明白的点这里 (英语渣慎入,不过程序猿中应该没有英语渣吧)

C++11中的std::async

注:std::async定义在future头文件中。

为什么大多数情况下使用async而不用thread

thread可以快速、方便地创建线程,但在async面前,就是小巫见大巫了。
async可以根据情况选择同步执行或创建新线程来异步执行,当然也可以手动选择。对于async的返回值操作也比thread更加方便。

std::async参数

不同于thread,async是一个函数,所以没有成员函数。

重载版本 作用
template <class Fn, class… Args>
  future<typename result_of<Fn(Args…)>::type>
    async (Fn&& fn, Args&&… args)
异步或同步(根据操作系统而定)以args为参数执行fn
同样地,传递引用参数需要std::refstd::cref
template <class Fn, class… Args>
  future<typename result_of<Fn(Args…)>::type>
    async (launch policy, Fn&& fn, Args&&… args);
异步或同步(根据policy参数而定(见下文))以args为参数执行fn,引用参数同上

std::launch强枚举类(enum class)

std::launch有2个枚举值和1个特殊值:

标识符 实际值(以Visual Studio 2019为标准) 作用
枚举值:launch::async 0x1(1) 异步启动
枚举值:launch::deferred 0x2(2) 在调用future::get、future::wait时同步启动(std::future见后文)
特殊值:launch::async | launch::defereed 0x3(3) 同步或异步,根据操作系统而定

例六:std::async的使用

暂且不管它的返回值std::future是啥,先举个例再说。

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <future>
using namespace std;
int main() {async(launch::async, [](const char *message){cout << message << flush;}, "Hello, ");cout << "World!" << endl;return 0;
}

你的编译器可能会给出一条警告:

warning C4834: 放弃具有 "nodiscard" 属性的函数的返回值

这是因为编译器不想让你丢弃async的返回值std::future,不过在这个例子中不需要它,忽略这个警告就行了。
你的输出结果:

Hello, World!

不过如果你输出的是

World!
Hello,

也别慌,正常现象,多线程嘛!反正我执行了好几次也没出现这个结果。

C++11中的std::future

我们已经知道如何使用async来异步或同步执行任务,但如何获得函数的返回值呢?这时候,async的返回值std::future就派上用场了。

例七:使用std::future获取线程的返回值

在之前的所有例子中,我们创建线程时调用的函数都没有返回值,但如果调用的函数有返回值呢?

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
// #include <thread> // 这里我们用async创建线程
#include <future> // std::async std::future
using namespace std;template<class ... Args> decltype(auto) sum(Args&&... args) {// C++17折叠表达式// "0 +"避免空参数包错误return (0 + ... + args);
}int main() {// 注:这里不能只写函数名sum,必须带模板参数future<int> val = async(launch::async, sum<int, int, int>, 1, 10, 100);// future::get() 阻塞等待线程结束并获得返回值cout << val.get() << endl;return 0;
}

输出:

111

代码解释

我们定义了一个函数sum,它可以计算多个数字的和,之后我们又定义了一个对象val,它的类型是std::future<int>,这里的int代表这个函数的返回值是int类型。在创建线程后,我们使用了future::get()来阻塞等待线程结束并获取其返回值。至于sum函数中的折叠表达式(fold expression),不是我们这篇文章的重点。

std::future常用成员函数

构造&析构函数

函数 类型 作用
future() noexcept 默认构造函数 构造一个空的、无效的future对象,但可以移动分配到另一个future对象
future(const future&) = delete 复制构造函数 (已删除)
future (future&& x) noexcept 移动构造函数 构造一个与x相同的对象并破坏x
~future() 析构函数 析构对象

常用成员函数

函数 作用
一般:T get()
当类型为引用:R& future<R&>::get()
当类型为void:void future::get()
阻塞等待线程结束并获取返回值。
若类型为void,则与future::wait()相同。
只能调用一次。
void wait() const 阻塞等待线程结束
template <class Rep, class Period>
  future_status wait_for(const chrono::duration<Rep,Period>& rel_time) const;
阻塞等待rel_timerel_time是一段时间),
若在这段时间内线程结束则返回future_status::ready
若没结束则返回future_status::timeout
若async是以launch::deferred启动的,则不会阻塞并立即返回future_status::deferred
不知道std::chrono::duration的点这里

std::future_status强枚举类

见上文future::wait_for解释

为啥要有void特化的std::future?

std::future的作用并不只有获取返回值,它还可以检测线程是否已结束、阻塞等待,所以对于返回值是void的线程来说,future也同样重要。

例八:void特化std::future

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <future>
using namespace std;
void count_big_number() {// C++14标准中,可以在数字中间加上单// 引号 ' 来分隔数字,使其可读性更强for (int i = 0; i <= 10'0000'0000; i++);
}
int main() {future<void> fut = async(launch::async, count_big_number);cout << "Please wait" << flush;// 每次等待1秒while (fut.wait_for(chrono::seconds(1)) != future_status::ready)cout << '.' << flush;cout << endl << "Finished!" << endl;return 0;
}

如果你运行一下这个代码,你也许就能搞懂那些软件的加载画面是怎么实现的。

C++11中的std::promise

在上文,我们已经讲到如何获取async创建线程的返回值。不过在某些特殊情况下,我们可能需要使用thread而不是async,那么如何获得thread的返回值呢?
如果你尝试这么写,那么你的编译器肯定会报错:

std::thread th(func);
std::future<int> return_value = th.join();

还记得之前我们讲的thread成员函数吗?thread::join()的返回值是void类型,所以你不能通过join来获得线程返回值。那么thread里有什么函数能获得返回值呢?
答案是:没有。
惊不惊喜?意不意外?thread竟然不能获取返回值!难道thread真的就没有办法返回点什么东西吗?如果你真是那么想的,那你就太低估C++了。一些聪明的人可能已经想到解决办法了:可以通过传递引用的方式来获取返回值。

例九:引用传递返回值

这个例子中我们先不牵扯多线程的问题。假如你写一个函数,需要返回3个值,那你会怎么办呢?vector?嵌套pair?不不不,都不需要,3个引用参数就可以了。

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
using namespace std;
constexpr long double PI = 3.14159265358979323846264338327950288419716939937510582097494459230781640628;
// 给定圆的半径r,求圆的直径、周长及面积
void get_circle_info(double r, double &d, double &c, double &s) {d = r * 2;c = PI * d;s = PI * r * r;
}
int main() {double r;cin >> r;double d, c, s;get_circle_info(r, d, c, s);cout << d << ' ' << c << ' ' <<  s << endl;return 0;
}

输入5,输出:

10 31.4159 78.5398

如果你和我输出有一些误差,是正常现象,不同编译器、不同机器处理精度也有所不同

std::promise到底是啥

promise实际上是std::future的一个包装,在讲解future时,我们并没有牵扯到改变future值的问题,但是如果使用thread以引用传递返回值的话,就必须要改变future的值,那么该怎么办呢?
实际上,future的值不能被改变,但你可以通过promise来创建一个拥有特定值的future。什么?没听懂?好吧,那我就举个例子:

例十:std::future的值不能改变,那么如何利用引用传递返回值

constexpr int a = 1;

现在,把常量当成future,把a当作一个future对象,那我们想拥有一个值为2的future对象该怎么办?
很简单:

constexpr int a = 1;
constexpr int b = 2;

这样,我们就不用思考如何改动a的值,直接创建一个新常量就能解决问题了。
promise的原理就是这样,不改变已有future的值,而是创建新的future对象。什么?还没听懂?好吧,记住这句话:

future的值不能改变,promise的值可以改变。

std::promise常用成员函数

构造&析构函数

函数 类型 作用
promise() 默认构造函数 构造一个空的promise对象
template <class Alloc> promise(allocator_arg_t aa, const Alloc& alloc) 构造函数 与默认构造函数相同,但使用特定的内存分配器alloc构造对象
promise (const promise&) = delete 复制构造函数 (已删除)
promise (promise&& x) noexcept 移动构造函数 构造一个与x相同的对象并破坏x
~promise() 析构函数 析构对象

常用成员函数

函数 作用
一般:
void set_value (const T& val)
void set_value (T&& val)
当类型为引用:void promise<R&>::set_value (R& val)
当类型为void:void promise::set_value (void)
设置promise的值并将共享状态设为ready(将future_status设为ready)
void特化:只将共享状态设为ready
future get_future() 构造一个future对象,其值与promise相同,status也与promise相同

例十一:std::promise的使用

以例七中的代码为基础加以修改:

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <future> // std::promise std::future
using namespace std;template<class ... Args> decltype(auto) sum(Args&&... args) {return (0 + ... + args);
}template<class ... Args> void sum_thread(promise<long long> &val, Args&&... args) {val.set_value(sum(args...));
}int main() {promise<long long> sum_value;thread get_sum(sum_thread<int, int, int>, ref(sum_value), 1, 10, 100);cout << sum_value.get_future().get() << endl;get_sum.join(); // 感谢评论区 未来想做游戏 的提醒return 0;
}

输出:

111

C++11中的std::this_thread

上面讲了那么多关于创建、控制线程的方法,现在该讲讲关于线程控制自己的方法了。
<thread>头文件中,不仅有std::thread这个类,而且还有一个std::this_thread命名空间,它可以很方便地让线程对自己进行控制。

std::this_thread常用函数

std::this_thread是个命名空间,所以你可以使用using namespace std::this_thread;这样的语句来展开这个命名空间,不过我不建议这么做。

函数 作用
std::thread::id get_id() noexcept 获取当前线程id
template<class Rep, class Period>
void sleep_for( const std::chrono::duration<Rep, Period>& sleep_duration )
等待sleep_durationsleep_duration是一段时间)
void yield() noexcept 暂时放弃线程的执行,将主动权交给其他线程
(放心,主动权还会回来)

例十二:std::this_thread中常用函数的使用

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;
atomic_bool ready = 0;
// uintmax_t ==> unsigned long long
void sleep(uintmax_t ms) {this_thread::sleep_for(chrono::milliseconds(ms));
}
void count() {while (!ready) this_thread::yield();for (int i = 0; i <= 20'0000'0000; i++);cout << "Thread " << this_thread::get_id() << " finished!" << endl;return;
}
int main() {thread th[10];for (int i = 0; i < 10; i++)th[i] = thread(::count);sleep(5000);ready = true;cout << "Start!" << endl;for (int i = 0; i < 10; i++)th[i].join();return 0;
}

我的输出:

Start!
Thread 8820 finished!Thread 6676 finished!Thread 13720 finished!
Thread 3148 finished!
Thread 13716 finished!
Thread 16424 finished!
Thread 14228 finished!
Thread 15464 finished!
Thread 3348 finished!
Thread 6804 finished!

你的输出几乎不可能和我一样,不仅是多线程并行的问题,而且每个线程的id也可能不同。

结尾

这篇文章到这里就结束了 (说不定以后还会写个c++20的std::jthread讲解)。 感谢各位在评论区提出的建议。 这是我第一篇接近2万字的文章。其实我刚开始写这篇文章时,也没想到这篇文章会吸引这么多人看,评论里还会有很多的好评,并且还上过一次热榜:

(厚颜无耻地给自己点赞)
又入选过C/C++领域内容榜:

这着实是出乎我的意料的。在此也感谢评论区里各位的好评,我就不一一回复了。
如果你觉得这篇文章有不对、不标准之处,也可以在评论区里说一下,感谢支持。

C++11 多线程(std::thread)详解相关推荐

  1. C++11 并发指南------std::thread 详解

    参考: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Int ...

  2. 【C/C++开发】C++11 并发指南二(std::thread 详解)

    上一篇博客<C++11 并发指南一(C++11 多线程初探)>中只是提到了 std::thread 的基本用法,并给出了一个最简单的例子,本文将稍微详细地介绍 std::thread 的用 ...

  3. C++11 并发指南二(std::thread 详解)

    上一篇博客<C++11 并发指南一(C++11 多线程初探)>中只是提到了 std::thread 的基本用法,并给出了一个最简单的例子,本文将稍微详细地介绍 std::thread 的用 ...

  4. std::thread详解

    转自:http://www.cnblogs.com/haippy/p/3236136.html 上一篇博客<C++11 并发指南一(C++11 多线程初探)>中只是提到了 std::thr ...

  5. 【转】C++11 并发指南五(std::condition_variable 详解)

    http://www.cnblogs.com/haippy/p/3252041.html 前面三讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三 ...

  6. C++11 并发指南五(std::condition_variable 详解)

    前面三讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread,std::mut ...

  7. C++11 并发指南三(std::mutex 详解)

    上一篇<C++11 并发指南二(std::thread 详解)>中主要讲到了 std::thread 的一些用法,并给出了两个小例子,本文将介绍 std::mutex 的用法. Mutex ...

  8. c+++11并发编程语言,C++11并发编程:多线程std:thread

    原标题:C++11并发编程:多线程std:thread 一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植, ...

  9. springboot主线程_Springboot对多线程的支持详解

    Springboot对多线程的支持详解 这两天看阿里的JAVA开发手册,到多线程的时候说永远不要用 new Thread()这种方式来使用多线程.确实是这样的,我一直在用线程池,到了springboo ...

  10. python爬虫多线程是什么意思_python爬虫中多线程的使用详解

    queue介绍 queue是python的标准库,俗称队列.可以直接import引用,在python2.x中,模块名为Queue.python3直接queue即可 在python中,多个线程之间的数据 ...

最新文章

  1. CodeForces - 1029B.Creating the Contest(最长上升子序列0(n)解法)
  2. 台式电脑耳机插孔在哪_不到一千元的迷你电脑究竟如何?Intel NUC7CJYH测评来了...
  3. 深入基础(一)模块化
  4. 9个不为人知的Python技巧
  5. Unity图片优化神器 - dither算法究极进化方案
  6. Mysql批量更新的一个坑-allowMultiQueries=true允许批量更新
  7. docker -v 覆盖了容器中的文件_Docker容器之安装Mysql
  8. 银行固定资产管理系统盘点方案
  9. 森松尼N-J60双模机械键盘按键操作说明
  10. 玩转用户身份权益——详解闲鱼身份权益体系的实现
  11. 力扣题库63-不同路径II(C语言)
  12. php正则中英文数字,PHP正则匹配中英文、数字及下划线方法
  13. pycharm 2017年5月注册码(2017.4.25更新)
  14. Java版数据结构之单向链表
  15. Base64中文乱码解决方法
  16. ios 开发 怎样在项目中使用除系统外的字体
  17. Java的输入/输出
  18. 高斯混合模型的终极理解
  19. QNX Neutrino IPL
  20. 局域网共享故障的分析与排除

热门文章

  1. c语言水仙花数while程序,水仙花数--C语言,for循环,while 循环
  2. 机械手引导视觉系统定位抓取
  3. Matlab 常用语句
  4. Git使用教程:最详细、最傻瓜、最浅显、真正手把手教!最新版!!!
  5. 芯昇 CM32M101A 固件库 W25Q128JWSIQ 驱动
  6. 2013年CISA考试知识点变化总结讲义
  7. 2021年11月中国商品出口总额排行榜:中国贸易顺差717.1亿美元,7个出口最终目的国(地)出口额超过百亿美元(附月榜TOP100详单)
  8. 如何给文件夹加密【转】
  9. python十六进制和十进制相互转换以及由补码求原码
  10. 华为与IBM员工考核体系的差别