打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C 11 thread

C++11 thread

分类: c/c++72人阅读评论(0)收藏举报
windows系统中,需要vs2010+sp1或vs2012才支持。


1.线程的创建
C++11线程类std::thread,头文件include <thread>
首先,看一个最简单的例子:

  1. void my_thread()  
  2. {  
  3.     puts("hello, world");  
  4. }  
  5.   
  6. int main(int argc, char *argv[])  
  7. {  
  8.     std::thread t(my_thread);  
  9.     t.join();  
  10.   
  11.     system("pause");  
  12.     return 0;  
  13. }  

实例化一个线程对象t,参数my_thread是一个函数,在线程创建完成后将被执行,
t.join()等待子线程my_thread执行完之后,主线程才可以继续执行下去,此时主线程会
释放掉执行完后的子线程资源。


当然,如果不想等待子线程,可以在主线程里面执行t.detach()将子线程从主线程里分离,
子线程执行完成后会自己释放掉资源。分离后的线程,主线程将对它没有控制权了。
相对于以前使用过的beginthread传多个参数需要传入struct地址,
boost::thread传参需要bind,std::thread传参真的非常方便,而且可读性也很好。
下面例子在实例化线程对象的时候,在线程函数my_thread后面紧接着传入两个参数。

  1. #include <iostream>  
  2. #include <stdlib.h>  
  3. #include <thread>  
  4. #include <string>  
  5.   
  6. void my_thread(int num, const std::string& str)  
  7. {  
  8.     std::cout << "num:" << num << ",name:" << str << std::endl;  
  9. }  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int num = 1234;  
  14.     std::string str = "tujiaw";  
  15.     std::thread t(my_thread, num, str);  
  16.     t.detach();  
  17.       
  18.     system("pause");  
  19.     return 0;  
  20. }  


2.互斥量
多个线程同时访问共享资源的时候需要需要用到互斥量,当一个线程锁住了互斥量后,其他线程必须等待这个互斥量解锁后才能访问它。thread提供了四种不同的互斥量:
独占式互斥量non-recursive (std::mutex)
递归式互斥量recursive (std::recursive_mutex)
允许超时的独占式互斥量non-recursive that allows timeouts on the lock functions(std::timed_mutex)
允许超时的递归式互斥量recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)


独占式互斥量
独占式互斥量加解锁是成对的,同一个线程内独占式互斥量在没有解锁的情况下,再次对它进行加锁这是不对的,会得到一个未定义行为。
如果你想thread1输出10次10,thread2输出10次20,下面程序是做不到的,因为在thread1输出的时候,
thread2也会执行,它会改变g_num的值,所以我们需要在它们访问共享资源的时候使用互斥量加锁。打开代码里面的三行注释就可以得到正确的结果了。在线程1中std::mutex使用成员函数lock加锁unlock解锁,看起来工作的很好,但这样是不安全的,你得始终记住lock之后一定要unlock,但是如果在它们中间出现了异常或者线程直接退出了unlock就没有执行,因为这个互斥量是独占式的,所以在thread1没有解锁之前,其他使用这个互斥量加锁的线程会一直处于等待状态得不到执行。lock_guard模板类使用RAII手法封装互斥量,在实例化对象的时候帮你加锁,并且能保证在离开作用域的时候自动解锁,所以你应该用lock_guard来帮你加解锁。
  1. #include <iostream>  
  2. #include <stdlib.h>  
  3. #include <thread>  
  4. #include <string>  
  5. #include <mutex>  
  6.   
  7. int g_num = 0;  
  8. std::mutex g_mutex;  
  9.   
  10. void thread1()  
  11. {  
  12.     //g_mutex.lock();  
  13.     g_num = 10;  
  14.     for (int i=0; i<10; i++){  
  15.         std::cout << "thread1:" << g_num << std::endl;  
  16.     }  
  17.     //g_mutex.unlock();  
  18. }  
  19.   
  20. void thread2()  
  21. {  
  22.     //std::lock_guard<std::mutex> lg(g_mutex);  
  23.     g_num = 20;  
  24.     for (int i=0; i<10; i++){  
  25.         std::cout << "thread2:" << g_num << std::endl;  
  26.     }  
  27. }  
  28.   
  29. int main(int argc, char *argv[])  
  30. {  
  31.     std::thread t1(thread1);  
  32.     std::thread t2(thread2);  
  33.     t1.join();  
  34.     t2.join();  
  35.       
  36.     system("pause");  
  37.     return 0;  
  38. }  



递归式互斥量
与独占式互斥量不同的是,同一个线程内在互斥量没有解锁的情况下可以再次进行加锁,不过他们的加解锁次数需要一致,递归式互斥量我们平时可能用得比较少些。


允许超时的互斥量
如果线程1对共享资源的访问时间比较长,这时线程2可能等不了那么久,所以设置一个超时时间,在超时时间内如果线程1中的互斥量还没有解锁,线程2就不等了,继续往下执行。
lock_guard只是提供了对互斥量最基本的加解锁封装,而unique_lock提供了多种构造方法,使用起来更加灵活,对于允许超时的互斥量需要使用unnique_lock来包装。
  1. std::timed_mutex g_timed_mutex;  
  2. void thread1()  
  3. {  
  4.     std::unique_lock<std::timed_mutex> tl(g_timed_mutex);  
  5.     ::Sleep(3000); // 睡眠3秒  
  6.     puts("thread1");  
  7. }  
  8.   
  9. void thread2()  
  10. {  
  11.     std::unique_lock<std::timed_mutex> tl(g_timed_mutex, std::chrono::milliseconds(1000)); // 超时时间1秒  
  12.     puts("thread2");  
  13. }  
  14.   
  15. int main(int argc, char *argv[])  
  16. {  
  17.     std::thread t1(thread1);  
  18.     ::Sleep(100); // 让线程1先启动  
  19.     std::thread t2(thread2);  
  20.     t1.join();  
  21.     t2.join();  
  22.       
  23.     system("pause");  
  24.     return 0;  
  25. }  


注意死锁
有时,一个操作需要对一个以上的mutex加锁,这时请注意了,这样很可能造成死锁。
  1. struct Widget  
  2. {  
  3.     std::mutex mutex_;  
  4.     std::string str_;  
  5. };  
  6.   
  7. void foo(Widget& w1, Widget& w2)  
  8. {  
  9.     std::unique_lock<std::mutex> t1(w1.mutex_);  
  10.     std::unique_lock<std::mutex> t2(w2.mutex_);  
  11.     // do something  
  12. }  
  13. Widget g_w1, g_w2;  

当一个线程调用foo(g_w1, g_w2),另外一个线程调用foo(g_w2, g_w1)的时候,
线程1:                线程2:
w1.mutex_.lock         ...
...                    w2.mutex_.lock
...                    ...
w2.mutex_.lock等待     ...
                       w1.mutex_lock等待
可能的执行顺序:
线程1中的w1上锁;
线程2中的w2上锁;
线程1中的w2上锁,此时由于w2已经在线程2中上过锁了,所以必须等待;
线程2中的w1上锁,此时由于w1已经在线程1中上过锁了,所以必须等待;
这样两个线程都等不到对方释放锁,都处于等待状态造成了死锁。
thread提供了一个std::lock函数可以对多个互斥量同时加锁,每个线程里面的
w1和w2会同时上锁,他们之间就没有间隙了,如上将foo函数改为如下形式就可以了:
  1. void foo(Widget& w1, Widget& w2)  
  2. {  
  3.     std::unique_lock<std::mutex> t1(w1.mutex_, std::defer_lock);  
  4.     std::unique_lock<std::mutex> t2(w2.mutex_, std::defer_lock);  
  5.     std::lock(t1, t2);  
  6.     // do something  
  7. }  

在实例化的时候先不要加锁,等到两个对象都创建完成之后再一起加锁。


在初始化的时候保护数据
如果你的数据仅仅只在初始化的时候进行保护,使用一个互斥量是不行的,在初始化完成后会导致没必要的同步,C++11提供了一些方法来解决这个问题。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
聊聊C++中的Mutex,以及拯救生产力的Boost
Scope Lock模式线程Mutex
C++11多线程std::thread的简单使用
有了互斥量,为什么还需要条件变量?
Linux多线程——使用互斥量同步线程
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服