C++并发学习笔记(二)(std::thread 详解)

std::thread 构造

std::thread 在 <thread> 头文件中声明,因此使用 std::thread 时需要包含<thread>头文件。

default thread() noexcept; 默认构造函数,创建一个空的 thread 执行对象
initialization template <class Fn, class… Args> explicit thread (Fn&& fn, Args&&… args); 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
copy [deleted] thread (const thread&) = delete; 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
move thread (thread&& x) noexcept; move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象

注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.

Example (reference):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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 1 executing\n";
++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
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
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '\n';
}
1
2
3
4
5
6
7
8
9
10
11
Thread 1 executing
Thread 2 executing
Thread 1 executing
Thread 2 executing
Thread 1 executing
Thread 2 executing
Thread 1 executing
Thread 2 executing
Thread 1 executing
Thread 2 executing
Final value of n is 5

move 赋值操作

move thread& operator= (thread&& rhs) noexcept; move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
copy [deleted] thread& operator= (const thread&) = delete; 拷贝赋值操作被禁用,thread 对象不可被拷贝。

Example :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <thread>
#include <chrono>

void sub_task(int n)
{
std::this_thread::sleep_for(std::chrono::milliseconds(n));
std::cout << "hello world "
<< std::this_thread::get_id()
<< " paused " << n << " milliseconds" << std::endl;
}

int main(int argc, char** argv )
{
std::thread threads[5];
std::cout << "Spawning 5 threads...\n";
for (size_t i = 0; i < 5; i++)
threads[i] = std::thread(sub_task, i);

std::cout << "Done spawning threads! Now wait for them to join\n";

for (auto &t : threads)
t.join();

std::cout << "All threads joined.\n";


system("pause");
return 0;
}

其他成员函数

get_id

get_id获取线程 ID
若该线程是joinable的,该函数返回该线程ID
若该线程不是joinable的,该函数返回成员默认类型thread::id

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// thread::get_id / this_thread::get_id
#include <iostream> // std::cout
#include <thread> // std::thread, std::thread::id, std::this_thread::get_id
#include <chrono> // std::chrono::seconds

std::thread::id main_thread_id = std::this_thread::get_id();

void is_main_thread() {
if ( main_thread_id == std::this_thread::get_id() )
std::cout << "This is the main thread.\n";
else
std::cout << "This is not the main thread.\n";
}

int main()
{
is_main_thread();
std::thread th (is_main_thread);
th.join();
return 0;
}

Output:

1
2
This is the main thread.
This is not the main thread.

joinable

joinable 检查线程是否可被 join

线程可被join:当其为可执行线程时。

线程不可被join:

  • 1、线程是**default-constructed**(默认构造)
  • 2、当其被moved
  • 3、当其成员joindetach被调用时

当去销毁一个仍然可以“joinable”的C++线程对象会被认为是一种错误。为了销毁一个C++线程对象,约么join()函数需要被调用(并结束),要么detach()函数被调用。如果一个C++线程对象当销毁时仍然可以被join,异常会被抛出。

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>       // std::cout
#include <thread> // std::thread

void mythread()
{
// do stuff...
}

int main()
{
std::thread foo;
std::thread bar(mythread);

std::cout << "Joinable after construction:\n" << std::boolalpha;
std::cout << "foo: " << foo.joinable() << '\n';
std::cout << "bar: " << bar.joinable() << '\n';

if (foo.joinable()) foo.join();
if (bar.joinable()) bar.join();

std::cout << "Joinable after joining:\n" << std::boolalpha;
std::cout << "foo: " << foo.joinable() << '\n';
std::cout << "bar: " << bar.joinable() << '\n';

return 0;
}

Output(after 3 seconds):

1
2
3
4
5
6
Joinable after construction:
foo: false
bar: true
Joinable after joining:
foo: false
bar: false

join

joinJoin线程

当thread::join()函数被调用后,调用它的线程会被block,直到线程的执行被完成。基本上,这是一种可以用来知道一个线程已结束的机制。当thread::join()返回时,OS的执行的线程已经完成,C++线程对象可以被销毁。

Exapmle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>       // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds

void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}

int main()
{
std::cout << "Spawning 3 threads...\n";
std::thread t1 (pause_thread,1);
std::thread t2 (pause_thread,2);
std::thread t3 (pause_thread,3);
std::cout << "Done spawning threads. Now waiting for them to join:\n";
t1.join();
t2.join();
t3.join();
std::cout << "All threads joined!\n";

return 0;
}

Output (after 3 seconds):

1
2
3
4
5
6
Spawning 3 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
All threads joined!

detach

detachDetach线程

detach允许线程独立运行,线程结束,资源会释放。

当thread::detach()函数被调用后,执行的线程从线程对象中被分离,已不再被一个线程对象所表达–这是两个独立的事情。C++线程对象可以被销毁,同时OS执行的线程可以继续。如果程序想要知道执行的线程何时结束,就需要一些其它的机制。join()函数在那个thread对象上不能再被调用,因为它已经不再和一个执行的线程相关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>       // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds

void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}

int main()
{
std::cout << "Spawning and detaching 3 threads...\n";
std::thread (pause_thread,1).detach();
std::thread (pause_thread,2).detach();
std::thread (pause_thread,3).detach();
std::cout << "Done spawning threads.\n";

std::cout << "(the main thread will now pause for 5 seconds)\n";
// give the detached threads time to finish (but not guaranteed!):
pause_thread(5);
return 0;
}

Output:

1
2
3
4
5
6
7
Spawning and detaching 3 threads...
Done spawning threads.
(the main thread will now pause for 5 seconds)
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 5 seconds ended

swap

swapSwap线程

互换两个 thread 对象的底层句柄

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#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();

return 0;
}

Output:

1
2
3
4
5
6
7
8
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

native_handle返回 native handle 表示线程的实现定义句柄类型

Example: 在 POSIX 系统上用 native_handle 启用 C++ 线程的实时调度

只有库函数支持该函数时该方法才会有效,用于获得与操作系统相关的原生线程句柄

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>  
#include <thread>
#include <ctime>
using namespace std;
void show(int n){
cout<<"n="<<n<<endl;
}
int main()
{
thread t(show,18);
cout<<"t.get_id="<<t.get_id()<<endl;
auto tn=t.native_handle();
t.join();
cout<<"tn="<<tn<<endl;

return 0;
}

Output:

1
2
3
t.get_id=n=2
18
tn=2

hardware_concurrency [static]

hardware_concurrency (static)检测硬件并发特性

公共静态成员函数、检测硬件的并发特性、返回硬件线程上下文的数量

返回值:支持的并发线程数。若值不可计算,则返回 0;

Example:

1
2
3
4
5
6
7
#include <iostream>
#include <thread>

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

Output:

1
8 concurrent threads are supported.