C++11 并发指南------std::thread 详解
本节将详细介绍 std::thread
的用法。
std::thread
在 <thread>
头文件中声明,因此使用 std::thread
需包含 <thread>
头文件。
<thread>
头文件摘要
<thread>
头文件声明了 std::thread 线程类及 std::swap
(交换两个线程对象)辅助函数。另外命名空间 std::this_thread
也声明在 <thread>
头文件中。下面是 C++11 标准所定义的 <thread>
头文件摘要:
参见 N3242=11-0012 草案第 30.3 节 Threads(p1133)。
namespace std {
#define __STDCPP_THREADS__ __cplusplus
class thread;
void swap(thread& x, thread& y);
namespace this_thread {
thread::id get_id();
void yield();
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time);
}
}
<thread>
头文件主要声明了 std::thread
类,另外在 std::this_thread
命名空间中声明了get_id
,yield
,sleep_until
以及 sleep_for
等辅助函数,本章稍微会详细介绍 std::thread
类及相关函数。
std::thread
类摘要
std::thread
代表了一个线程对象,C++11 标准声明如下:
namespace std {
class thread {
public:
// 类型声明:
class id;
typedef implementation-defined native_handle_type;
// 构造函数、拷贝构造函数和析构函数声明:
thread() noexcept;
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
thread(thread&&) noexcept;
thread& operator=(const thread&) = delete;
thread& operator=(thread&&) noexcept;
// 成员函数声明:
void swap(thread&) noexcept;
bool joinable() const noexcept;
void join();
void detach();
id get_id() const noexcept;
native_handle_type native_handle();
// 静态成员函数声明:
static unsigned hardware_concurrency() noexcept;
};
}
std::thread
中主要声明三类函数:(1). 构造函数、拷贝构造函数及析构函数;(2). 成员函数;(3). 静态成员函数。另外,std::thread::id
表示线程 ID,同时 C++11 声明如下:
namespace std {
class thread::id {
public:
id() noexcept;
};
bool operator==(thread::id x, thread::id y) noexcept;
bool operator!=(thread::id x, thread::id y) noexcept;
bool operator<(thread::id x, thread::id y) noexcept;
bool operator<=(thread::id x, thread::id y) noexcept;
bool operator>(thread::id x, thread::id y) noexcept;
bool operator>=(thread::id x, thread::id y) noexcept;
template<class charT, class traits>
basic_ostream<charT, traits>&
operator<< (basic_ostream<charT, traits>& out, thread::id id);
// Hash 支持
template <class T> struct hash;
template <> struct hash<thread::id>;
}
std::thread
详解
std::thread
构造和赋值
std::thread
构造函数
默认构造函数 (1) | thread() noexcept; |
---|---|
初始化构造函数 (2) | template <class Fn, class... Args> explicit thread(Fn&& fn, Args&&... args); |
拷贝构造函数 [deleted] (3) | thread(const thread&) = delete; |
Move 构造函数 (4) | thread(thread&& x) noexcept; |
- 默认构造函数(1),创建一个空的
std::thread
执行对象。 - 初始化构造函数(2),创建一个
std::thread
对象,该std::thread
对象可被joinable
,新产生的线程会调用fn
函数,该函数的参数由args
给出。 - 拷贝构造函数(被禁用)(3),意味着
std::thread
对象不可拷贝构造。 - Move 构造函数(4),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
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';
}
std::thread
赋值操作
Move 赋值操作 (1) | thread& operator=(thread&& rhs) noexcept; |
---|---|
拷贝赋值操作 [deleted] (2) | thread& operator=(const thread&) = delete; |
- Move 赋值操作(1),如果当前对象不可
joinable
,需要传递一个右值引用(rhs
)给move
赋值操作;如果当前对象可被joinable
,则会调用terminate
() 报错。 - 拷贝赋值操作(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_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;
}
其他成员函数
本小节例子来自 http://en.cppreference.com
get_id
: 获取线程 ID,返回一个类型为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();
}
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: " << t.joinable() << '\n'; t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable() << '\n'; t.join();
}
join
: Join 线程,调用该函数会阻塞当前线程,直到由*this
所标示的线程执行完毕 join 才返回。#include <iostream>
#include <thread>
#include <chrono> void foo()
{
// simulate expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
} void bar()
{
// simulate expensive operation
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";
}
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));
}
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
native_handle
: 返回 native handle(由于std::thread
的实现和操作系统相关,因此该函数返回与std::thread
具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库)。#include <thread>
#include <iostream>
#include <chrono>
#include <cstring>
#include <pthread.h> std::mutex iomutex;
void f(int num)
{
std::this_thread::sleep_for(std::chrono::seconds(1)); sched_param sch;
int policy;
pthread_getschedparam(pthread_self(), &policy, &sch);
std::lock_guard<std::mutex> lk(iomutex);
std::cout << "Thread " << num << " is executing at priority "
<< sch.sched_priority << '\n';
} int main()
{
std::thread t1(f, 1), t2(f, 2); sched_param sch;
int policy;
pthread_getschedparam(t1.native_handle(), &policy, &sch);
sch.sched_priority = 20;
if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) {
std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n';
} t1.join();
t2.join();
}
执行结果如下:
Thread 2 is executing at priority 0
Thread 1 is executing at priority 20
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";
}
std::this_thread
命名空间中相关辅助函数介绍
get_id: 获取线程 ID。
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex> std::mutex g_display_mutex; void foo()
{
std::thread::id this_id = std::this_thread::get_id(); g_display_mutex.lock();
std::cout << "thread " << this_id << " sleeping...\n";
g_display_mutex.unlock(); std::this_thread::sleep_for(std::chrono::seconds(1));
} int main()
{
std::thread t1(foo);
std::thread t2(foo); t1.join();
t2.join();
}
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();
} 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";
}
sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比
sleep_duration
所表示的时间片更长。template< class Rep, class Period >
void sleep_for( const std::chrono::duration<Rep,Period>& 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";
}
执行结果如下:
Hello waiter
Waited 2000 ms
C++11 并发指南------std::thread 详解的更多相关文章
- 转 C++11 并发指南std::condition_variable详解
之前看过,但是一直没有怎么用就忘了,转一篇别人的文字记录下来 本文将介绍 C++11 标准中 <condition_variable> 头文件里面的类和相关函数. <conditio ...
- C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)
上一讲<C++11 并发指南四(<future> 详解一 std::promise 介绍)>主要介绍了 <future> 头文件中的 std::promise 类, ...
- C++11 并发指南四(<future> 详解三 std::future & std::shared_future)
上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...
- C++11 并发指南四(<future> 详解三 std::future & std::shared_future)(转)
上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...
- C++11 并发指南三(Lock 详解)(转载)
multithreading 多线程 C++11 C++11多线程基本使用 C++11 并发指南三(Lock 详解) 在 <C++11 并发指南三(std::mutex 详解)>一文中我们 ...
- C++11 并发指南四(<future> 详解一 std::promise 介绍)
前面两讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread 和 std::m ...
- C++11 并发指南四(<future> 详解一 std::promise 介绍)(转)
前面两讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread 和 std::m ...
- C++11 并发指南三(Lock 详解)
在 <C++11 并发指南三(std::mutex 详解)>一文中我们主要介绍了 C++11 标准中的互斥量(Mutex),并简单介绍了一下两种锁类型.本节将详细介绍一下 C++11 标准 ...
- C++11 并发指南二(std::thread 详解)
上一篇博客<C++11 并发指南一(C++11 多线程初探)>中只是提到了 std::thread 的基本用法,并给出了一个最简单的例子,本文将稍微详细地介绍 std::thread 的用 ...
随机推荐
- SpringJUnit4加载类目录下(src)和WEF-INF目录下的配置文件
路径说明: 一.加载类目录下的配置文件 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:ap ...
- checkbox复选框样式
随着现代浏览器的流行,纯CSS设置checkbox也变的很是实用,下面会讲到5种与众不同的checkbox复选框. 首先,需要添加一段CSS隐藏所有的Checkbox复选框,下面我们会改变它的外观.要 ...
- Google Play市场考察报告
考察了Google Play日本市场的10款应用,考察的重点在于每个App有什么亮点,盈利模式在哪里.本文并不是App的功能介绍. (1)恋爱文集[文库类应用] 该应用收录了一些恋爱文章,其主要受众是 ...
- xcode 树形管理 cocos2d-x的资源
把资源以目录的形式加入xcode中, 同时, 在加入时, 选择"Create Folder References for any added folders", 而不是默认的&q ...
- 对Delphi控件作用的新理解(控件本身的源代码就是一个很强的工业级源码)
最近几天,对Delphi控件的含义有了一个新的理解.其实它不仅仅是给程序员提供功能的一个表层调用,控件本身的源代码就是一个很强的工业级源码.而且它的Main例子,往往就已经是半成品.而别的语言里没有那 ...
- 不同VLAN之间互相通信
前话 我们经常到机房上课,想必对机房后面那层叠的跟DVD一样的机器有印象吧,那些就是交换机. 交换机作用是什么? 我这里度娘一下: 交换机(Switch)意为"开关"是一种用于电( ...
- Git教程(9)集中式工作方式常用的设计分支的方案
Git是一个复杂的版本管理系统,管理代码有很多工作方式,如集中式,管理者式,司令/副官式 本文是假设选用集中式工作方式时,设计分支的方案. 中小型项目: 维护两个长期分支,分别是master 和 de ...
- 关于捕获键盘信息的processDialogkey方法2--具体应用
自定义控件里的keydown方法无法捕获所有的按键消息的处理方法1(自定义控件里的keydown方法无法获取的键值如上下左右键等) 处理办法具体如下: 1.首先在自定义控件UserControl1中重 ...
- 【HDOJ】4358 Boring counting
基本思路是将树形结构转线性结构,因为查询的是从任意结点到叶子结点的路径.从而将每个查询转换成区间,表示从该结点到叶子结点的路径.离线做,按照右边界升序排序.利用树状数组区间修改.树状数组表示有K个数据 ...
- Where is Vasya?
Where is Vasya? Vasya stands in line with number of people p (including Vasya), but he doesn't know ...