• C++11并发学习之三:线程同步(转载)


    C++11并发学习之三:线程同步

    1.<mutex> 头文件介绍 

    Mutex又称互斥量,C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在 <mutex> 头文件中,所以如果你需要使用 std::mutex,就必须包含 <mutex> 头文件。

    (1)Mutex系列类(四种)
    std::mutex,最基本的 Mutex 类。
    std::recursive_mutex,递归 Mutex 类。
    std::time_mutex,定时 Mutex 类。

    std::recursive_timed_mutex,定时递归 Mutex 类。

    (2)Lock系列类(两种)
    std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
    std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

    (3)其他类型(结构体)

    std::adopt_lock_t——它的常量对象定义为constexpr adopt_lock_t adopt_lock {};// constexpr 是 C++11 中的新关键字)
    std::defer_lock_t——它的常量对象定义为constexpr defer_lock_t defer_lock {};// constexpr 是 C++11 中的新关键字)

    std::try_to_lock_t——它的常量对象定义为constexpr try_to_lock_t try_to_lock {};// constexpr 是 C++11 中的新关键字)

    (4)函数
    std::try_lock,尝试同时对多个互斥量上锁。
    std::lock,可以同时对多个互斥量上锁。

    std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。

    2.常用类型举例

    (1)std::mutex类

    ☆构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
    ☆lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:①如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。②如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。③如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
    ☆unlock(), 解锁,释放对互斥量的所有权。
    ☆try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况:① 如果该互斥量当前没有被锁住,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。②如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。③如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。

    不论是lock()还是try_lock()都需要和unlock()配套使用,下面举例说明lock()和try_lock()的区别。

    [cpp] view plain copy
     
    1. #include <thread>  
    2. #include <iostream>  
    3. #include <string>  
    4. #include <chrono>  
    5. #include <assert.h>  
    6. #include <mutex>  
    7. int counter=0;  
    8. std::mutex mtx;  
    9. void func()  
    10. {  
    11.     for (int i=0; i<10000; ++i)  
    12.     {  
    13.             mtx.lock();  
    14.             ++counter;  
    15.             mtx.unlock();  
    16.     }  
    17. }  
    18.   
    19. int main()  
    20. {  
    21.     std::thread workerThreads[10];  
    22.     for (int i=0; i<10; ++i)  
    23.     {  
    24.         workerThreads[i] = std::thread(func);  
    25.     }  
    26.     for (auto& workerThread : workerThreads)  
    27.     {  
    28.         workerThread.join();  
    29.     }  
    30.     std::cout << counter << " successful increases of the counter"<<std::endl;  
    31.   
    32.     return 0;  
    33. }  


    由于lock()的阻塞特性,所以每个线程都统计了10000次,一共是10*10000=100000次。

    [cpp] view plain copy
     
    1. #include <thread>  
    2. #include <iostream>  
    3. #include <string>  
    4. #include <chrono>  
    5. #include <assert.h>  
    6. #include <mutex>  
    7. int counter=0;  
    8. std::mutex mtx;  
    9. void func()  
    10. {  
    11.     for (int i=0; i<10000; ++i)  
    12.     {  
    13.         if (mtx.try_lock())  
    14.         {  
    15.             ++counter;  
    16.             mtx.unlock();  
    17.         }  
    18.     }  
    19. }  
    20.   
    21. int main()  
    22. {  
    23.     std::thread workerThreads[10];  
    24.     for (int i=0; i<10; ++i)  
    25.     {  
    26.         workerThreads[i] = std::thread(func);  
    27.     }  
    28.     for (auto& workerThread : workerThreads)  
    29.     {  
    30.         workerThread.join();  
    31.     }  
    32.     std::cout << counter << " successful increases of the counter"<<std::endl;  
    33.   
    34.     return 0;  
    35. }  


    由于try_lock()的非阻塞特性,如果当前互斥量被其他线程锁住,则当前try_lock()返回 false,此时counter并不会增加1。所以这十个线程的统计结果具有随机性,下次运行程序时,统计值不一定是16191。

    (2).std::lock_guard和std::unique_lock类

    std::lock_guard使用起来比较简单,除了构造函数外没有其他成员函数。
    std::unique_lock除了lock_guard的功能外,提供了更多的成员函数,相对来说更灵活一些。这些成员函数包括lock,try_lock,try_lock_for,try_lock_until、unlock等。

    std::unique_lock::lock——用它所管理的Mutex对象的 lock 函数。

    std::unique_lock::try_lock——用它所管理的Mutex对象的 try_lock函数。

    std::unique_lock::unlock——用它所管理的Mutex对象的 unlock函数。

    这两个类相比使用std::mutex的优势在于不用配对使用,无需担心忘记调用unlock而导致的程序死锁。

    [cpp] view plain copy
     
    1. #include <thread>  
    2. #include <iostream>  
    3. #include <string>  
    4. #include <chrono>  
    5. #include <assert.h>  
    6. #include <mutex>  
    7. int counter=0;  
    8. std::mutex mtx;  
    9. void func()  
    10. {  
    11.     for (int i=0; i<10000; ++i)  
    12.     {  
    13.         //将std::lock_guard替换成std::unique_lock,效果是一样的  
    14.         std::lock_guard<std::mutex> lck (mtx);  
    15.         ++counter;  
    16.     }  
    17. }  
    18.   
    19. int main()  
    20. {  
    21.     std::thread workerThreads[10];  
    22.     for (int i=0; i<10; ++i)  
    23.     {  
    24.         workerThreads[i] = std::thread(func);  
    25.     }  
    26.     for (auto& workerThread : workerThreads)  
    27.     {  
    28.         workerThread.join();  
    29.     }  
    30.     std::cout << counter << " successful increases of the counter"<<std::endl;  
    31.   
    32.     return 0;  
    33. }  

    std::uniqure_lock构造函数的第二个参数可以是std::defer_lock,std::try_to_lock或std::adopt_lock

    [cpp] view plain copy
     
    1. #include <thread>  
    2. #include <iostream>  
    3. #include <string>  
    4. #include <chrono>  
    5. #include <assert.h>  
    6. #include <mutex>  
    7. int counter=0;  
    8. std::mutex mtx;  
    9. void func()  
    10. {  
    11.     for (int i=0; i<10000; ++i)  
    12.     {  
    13.         mtx.lock();  
    14.         //注意此时Tag参数为std::adopt_lock表明当前线程已经获得了锁,  
    15.         //此后mtx对象的解锁操作交由unique_lock对象lck来管理,在lck的生命周期结束之后,  
    16.         //mtx对象会自动解锁。  
    17.         std::unique_lock<std::mutex> lck(mtx,std::adopt_lock);  
    18.         ++counter;  
    19.     }  
    20. }  
    21.   
    22. int main()  
    23. {  
    24.     std::thread workerThreads[10];  
    25.     for (int i=0; i<10; ++i)  
    26.     {  
    27.         workerThreads[i] = std::thread(func);  
    28.     }  
    29.     for (auto& workerThread : workerThreads)  
    30.     {  
    31.         workerThread.join();  
    32.     }  
    33.     std::cout << counter << " successful increases of the counter"<<std::endl;  
    34.   
    35.     return 0;  
    36. }  

    [cpp] view plain copy
     
    1. #include <chrono>  
    2. #include <assert.h>  
    3. #include <mutex>  
    4. int counter=0;  
    5. std::mutex mtx;  
    6. void func()  
    7. {  
    8.     for (int i=0; i<10000; ++i)  
    9.     {  
    10.         //注意此时Tag参数为std::defer_lock表明当前线程没有获得了锁,  
    11.         //需要通过lck的lock和unlock来加锁和解锁,  
    12.         std::unique_lock<std::mutex> lck(mtx,std::defer_lock);  
    13.         lck.lock();  
    14.         ++counter;  
    15.         lck.unlock();  
    16.     }  
    17. }  
    18.   
    19. int main()  
    20. {  
    21.     std::thread workerThreads[10];  
    22.     for (int i=0; i<10; ++i)  
    23.     {  
    24.         workerThreads[i] = std::thread(func);  
    25.     }  
    26.     for (auto& workerThread : workerThreads)  
    27.     {  
    28.         workerThread.join();  
    29.     }  
    30.     std::cout << counter << " successful increases of the counter"<<std::endl;  
    31.   
    32.     return 0;  
    33. }  

    参考链接:http://www.cnblogs.com/haippy/p/3346477.html

  • 相关阅读:
    由一次自建库迁移到阿里云RDS引发的性能问题。
    pycharm2017自建注册服务器
    linux 邮件工具利器sendEmail时效超好
    python利用smtplib和MIMETYPE发送邮件
    如何去除本地文件与svn服务器的关联
    【转】nginx提示:500 Internal Server Error错误的解决方法
    Eclipse SVN插件安装
    Weblogic用户名密码获取
    Io 异常: The Network Adapter could not establish the connection
    Oracle修改字段名、字段数据类型
  • 原文地址:https://www.cnblogs.com/Vae1990Silence/p/8686419.html
Copyright © 2020-2023  润新知