C++11线程使用总结

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

[cpp]  view plain  copy
  1. amespace std {  
  2.     #define __STDCPP_THREADS__ __cplusplus  
  3.     class thread;  
  4.     void swap(thread& x, thread& y);  
  5.     namespace this_thread {  
  6.         thread::id get_id();  
  7.         void yield();  
  8.         template <class Clock, class Duration>  
  9.         void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);  
  10.         template <class Rep, class Period>  
  11.         void sleep_for(const chrono::duration<Rep, Period>& rel_time);  
  12.     }          
  13.   
  14. }  

<thread> 头文件主要声明了 std::thread 类,另外在 std::this_thread 命名空间中声明了get_id,yield,sleep_until 以及 sleep_for 等辅助函数,本章稍微会详细介绍 std::thread 类及相关函数。

std::thread 类摘要

std::thread 代表了一个线程对象,C++11 标准声明如下:

[cpp]  view plain  copy
  1. namespace std {  
  2.     class thread {  
  3.         public:  
  4.             // 类型声明:  
  5.             class id;  
  6.             typedef implementation-defined native_handle_type;  
  7.   
  8.             // 构造函数、拷贝构造函数和析构函数声明:  
  9.             thread() noexcept;  
  10.             template <class F, class ...Args> explicit thread(F&& f, Args&&... args);  
  11.             ~thread();  
  12.             thread(const thread&) = delete;  
  13.             thread(thread&&) noexcept;  
  14.             thread& operator=(const thread&) = delete;  
  15.             thread& operator=(thread&&) noexcept;  
  16.   
  17.             // 成员函数声明:  
  18.             void swap(thread&) noexcept;  
  19.             bool joinable() const noexcept;  
  20.             void join();  
  21.             void detach();  
  22.             id get_id() const noexcept;  
  23.             native_handle_type native_handle();  
  24.   
  25.             // 静态成员函数声明:  
  26.             static unsigned hardware_concurrency() noexcept;  
  27.     };  
  28. }  

std::thread 中主要声明三类函数:(1). 构造函数、拷贝构造函数及析构函数;(2). 成员函数;(3). 静态成员函数。另外,std::thread::id 表示线程 ID,同时 C++11 声明如下:

[cpp]  view plain  copy
  1. namespace std {  
  2.     class thread::id {  
  3.         public:  
  4.             id() noexcept;  
  5.     };  
  6.   
  7.     bool operator==(thread::id x, thread::id y) noexcept;  
  8.     bool operator!=(thread::id x, thread::id y) noexcept;  
  9.     bool operator<(thread::id x, thread::id y) noexcept;  
  10.     bool operator<=(thread::id x, thread::id y) noexcept;  
  11.     bool operator>(thread::id x, thread::id y) noexcept;  
  12.     bool operator>=(thread::id x, thread::id y) noexcept;  
  13.   
  14.     template<class charT, class traits>  
  15.     basic_ostream<charT, traits>&  
  16.         operator<< (basic_ostream<charT, traits>& out, thread::id id);  
  17.   
  18.   
  19.     // Hash 支持  
  20.     template <class T> struct hash;  
  21.     template <> struct hash<thread::id>;  
  22. }  
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 执行对象。

线程状态:

       在一个线程的生存期内,可以在多种状态之间转换,不同的操作系统可以实现不同的线程模型,定义许多不同的线程状态,每个状态还可以包含多个子状态,但大体来说,如下几种状态是通用的:
1)就绪:参与调度,等待被执行,一旦被调度选中,立即开始执行
2)运行:占用CPU,正在运行中
3)休眠:暂不参与调度,等待特定事件发生

4)中止:已经运行完毕,等待回收线程资源

线程环境:
线程存在于进程之中,进程内所有全局资源对于内部每个线程都是可见的。
进程内典型全局资源如下:
1)代码区:这意味着当前进程空间内所有的可见的函数代码,对于每个线程来说,也是可见的
2)静态存储区:全局变量,静态空间
3)动态存储区:堆空间
线程内典型的局部资源:
1)本地栈空间:存放本线程的函数调用栈,函数内部的局部变量等
2)部分寄存器变量:线程下一步要执行代码的指针偏移量

        C++中的thread对象通常来说表达了执行的线程(thread of execution)。我在使用多线程的时候,发现很多情况下都是用join()函数,但是在使用detach的时候效果明显就是不一样了。

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

       当thread::detach()函数被调用后,执行的线程从线程对象中被分离,该线程被从主线程分离出去放置到后台执行。已不再被一个线程对象所表达--这是两个独立的事情。C++线程对象可以被销毁,同时OS执行的线程可以继续。如果程序想要知道执行的线程何时结束,就需要一些其它的机制。join()函数在那个thread对象上不能再被调用,因为它已经不再和一个执行的线程相关联。没有thread对象指向该线程而失去了对它的控制,当对象析构时线程会继续在后台执行,但是当主程序退出时并不能保证线程能执行完。如果没有良好的控制机制或者这种后台线程比较重要,最好不用detach而应该使用join。

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

mutex:

mutex是用来保证线程同步的,防止不同的线程同时操作同一个共享数据。

示例代码:

[cpp]  view plain  copy
  1. int cnt= 20;  
  2. mutex m;  
  3. void t1()  
  4. {  
  5.     while (cnt > 0)  
  6.     {      
  7.         m.lock();  
  8.           
  9.         if (cnt > 0)  
  10.         {  
  11.             --cnt;  
  12.             cout << cnt << endl;  
  13.         }  
  14.   
  15.         m.unlock();  
  16.     }  
  17. }  
  18. void t2()  
  19. {  
  20.     while (cnt > 0)  
  21.     {  
  22.         m.lock();  
  23.           
  24.         if (cnt > 0)  
  25.         {  
  26.             --cnt;  
  27.             cout << cnt << endl;  
  28.         }  
  29.   
  30.         m.unlock();  
  31.     }  
  32. }  
  33. int main()  
  34. {  
  35.       
  36.     thread th1(t1);  
  37.     thread th2(t2);  
  38.       
  39.     th1.join();  
  40.     th2.join();  
  41.   
  42.     return 0;  
  43. }  
        运行结果,cnt是依次递减的,没有因为多线程而打乱次序:

lock_guard:

使用lock_guard则相对安全,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构(unlock),不会因为某个线程异常退出而影响其他线程。

[cpp]  view plain  copy
  1. int cnt = 20;  
  2. mutex m;  
  3. void t1()  
  4. {  
  5.     while (cnt > 0)  
  6.     {      
  7.         lock_guard<mutex> lockGuard(m);  
  8.         if (cnt > 0)  
  9.         {  
  10.             --cnt;  
  11.             cout << cnt << endl;  
  12.         }  
  13.           
  14.     }  
  15. }  
  16. void t2()  
  17. {  
  18.     while (cnt > 0)  
  19.     {  
  20.         lock_guard<mutex> lockGuard(m);  
  21.         if (cnt > 0)  
  22.         {  
  23.             --cnt;  
  24.             cout << cnt << endl;  
  25.         }  
  26.       
  27.     }  
  28. }  

get_id:

获取线程 ID,返回一个类型为 std::thread::id 的对象

示例如下:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <thread>  
  3. #include <chrono>  
  4.   
  5. void foo()  
  6. {  
  7.     std::this_thread::sleep_for(std::chrono::seconds(1));  
  8. }  
  9.   
  10. int main()  
  11. {  
  12.     std::thread t1(foo);  
  13.     std::thread::id t1_id = t1.get_id();  
  14.   
  15.     std::thread t2(foo);  
  16.     std::thread::id t2_id = t2.get_id();  
  17.   
  18.     std::cout << "t1‘s id: " << t1_id << ‘\n‘;  
  19.     std::cout << "t2‘s id: " << t2_id << ‘\n‘;  
  20.   
  21.     t1.join();  
  22.     t2.join();  
  23. }  

sleep_until: 

        线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。

[cpp]  view plain  copy
  1. templateclass Clock, class Duration >  
  2. void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );  

sleep_for: 

     线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比sleep_duration 所表示的时间片更长。

[cpp]  view plain  copy
  1. templateclass Rep, class Period >  
  2. void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration );  
  3.   
  4. #include <iostream>  
  5. #include <chrono>  
  6. #include <thread>  
  7.   
  8. int main()  
  9. {  
  10.     std::cout << "waiter" << std::endl;  
  11.     std::chrono::milliseconds dura( 1000 );  
  12.     std::this_thread::sleep_for( dura );  
  13.     std::cout << "Waited 1000 ms\n";  
  14. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值