参考: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%AF%A6%E8%A7%A3

本节将详细介绍 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_idyieldsleep_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. 默认构造函数(1),创建一个空的 std::thread 执行对象。
  2. 初始化构造函数(2),创建一个 std::thread 对象,该 std::thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
  3. 拷贝构造函数(被禁用)(3),意味着 std::thread 对象不可拷贝构造。
  4. 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;
  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_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 函数之后:

  1. *this 不再代表任何的线程执行实例。
  2. joinable() == false
  3. 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

最新文章

  1. 4.5 .net core下直接执行SQL语句并生成DataTable
  2. Dos学习笔记(2)dos屏幕内容的复制
  3. 一行两端对齐justify-content
  4. Windows 2008下部署Exchange Server 2007
  5. asp.net中如何绑定combox下拉框数据(调用存储过程)
  6. oracle11g创建数据库最后一步确定时弹出无法创建目录
  7. linux/windows平台生成随机数的不同方法
  8. Android学习记录:ViewPager实现欢迎页
  9. Spring 自动装配及自动注册的相关配置
  10. vscode使用笔记
  11. Linux安装Python2.7.9
  12. H5_0008:链接分享图片和判断平台
  13. [Swift]LeetCode617. 合并二叉树 | Merge Two Binary Trees
  14. ubuntu 14.04 sudo apt-get 简单 安装 ffmpeg
  15. 【SVN】svn使用方法
  16. Java打印九九乘法表及倒打九九乘法表
  17. (考研)散列表和hashcode和hashmap
  18. Window Batch编程示例
  19. 动态页面技术JSP/EL/JSTL
  20. go语言基础之切片的创建和截取

热门文章

  1. 建立Clojure开发环境-使用IDEA和Leiningen
  2. Avro RPC 之 Protocol 定义和代码生成
  3. 【Unity3D】【NGUI】本地生成API文档
  4. ASP.NET 访问 MySql
  5. ORACLE 更新关联多张表
  6. JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet
  7. 188. Best Time to Buy and Sell Stock IV
  8. Linux的终端与进程
  9. JavaBean 内省API BeanUtils工具 泛型 xml xml约束
  10. bzoj4028