东方耀AI技术分享

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 1666|回复: 1
打印 上一主题 下一主题

[C/C++] 多线程共享变量解决3:锁mutex临界区

[复制链接]

1365

主题

1856

帖子

1万

积分

管理员

Rank: 10Rank: 10Rank: 10

积分
14441
QQ
跳转到指定楼层
楼主
发表于 2021-8-9 11:33:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式





// 问题抛出:多线程的坑,使用了共享变量
// http://www.ai111.vip/thread-1242-1-1.html
//解决1、三个线程共享了资源,把共享去掉即可 比较高效与简单的方法
//如果可能,必须要用的方法 不使用共享变量就可以了 多定义一下变量即可啊 最后汇总
// 原则:如果没有必要,线程间不要共享资源




//解决2:如果一定要使用共享变量呢?咋办?   从共享变量作文章  原子操作atomic
//std::atomic<int>
// http://www.ai111.vip/thread-1245-1-1.html
//还是会有问题:变量都是原子操作的变量,但是如果多个组合起来又未必是原子操作了!




//多线程共享变量解决3:锁mutex临界区




//解决3:如何正确的使用mutex?
//很容易用错 不把临界区暴露给使用者 而是在共享变量类内部用mutex进行保护
// mutable std::mutex m_mutex;  应该是一个mutable变量
// 容易出错点:1、忘记成对出现 2、重复调lock() 3、抛异常后无法调用unlock()了
// 调用了lock()一定要调用unlock()  联想到了 构造与析构函数 单独将mutex封装为一个Lock()类


// stl里有一个Lock()类的实现  std::lock_guard<std::mutex> lock(m_mutex);
// 还是会有bug:1、自己给自己转钱   2、一个线程里张三给李四转钱,另一个线程里李四给张三转钱
// 自己解决的思路:看张三与李四的 对象地址,按固定的顺序来加锁   估计stl底层实现也是这样的思路
// 真正的正确用法:在转钱函数里 void transfor_money(UserAccount& 张三,UserAccount& 李四, int money);
// std::lock("所有UserAccount对象的mutex都锁住")  
// std::lock_gurad<std::mutex> lockA(a.mutex, std::adopt_lock); adopt_lock表示:构造不锁了 析构解锁




  1. #include <iostream>
  2. //#include <boost/thread/thread.hpp>
  3. #include <thread>
  4. #include <vector>
  5. //#include <atomic>
  6. #include <mutex>



  7. class Counter{
  8. public:
  9.     Counter() : m_count(0) {}
  10.     void addCount() {
  11.         //从内存到寄存器 寄存器加1 写回内存
  12.         m_mutex.lock();
  13.         m_count++;
  14.         m_mutex.unlock();
  15.     }
  16.     int count() const {
  17.         return m_count;
  18.     }
  19. private:
  20.     int m_count;
  21.     mutable std::mutex m_mutex;  //临界区变量的定义
  22.     //std::atomic<int> m_count;   
  23.     //原子操作变量类型  3个操作合并最小单元的原子操作
  24.     // 原子操作的int类型
  25. };

  26. int work(int a){
  27.     //do something
  28.     return a + a;
  29. }


  30. template <typename Iter>
  31. void real_work(Counter& c, double& total_value, Iter b, Iter e){
  32.     for(;b!=e;++b){
  33.         total_value += work(*b);
  34.         c.addCount();
  35.     }
  36. }

  37. bool is_work_finished(Counter& c, int max_times){
  38.     if(c.count() == max_times){
  39.         std::cout << "work工作已经完成啦!\n" << std::endl;
  40.         return true;
  41.     }
  42.     //std::cout << "work工作_没有完成呢" << std::endl;
  43.     return false;
  44. }



  45. int main(){

  46.     std::cout << "最大并发数=" << std::thread::hardware_concurrency() << std::endl;
  47.     // Counter c1;
  48.     // std::cout << c1.count()<< std::endl;
  49.     // c1.addCount();
  50.     // c1.addCount();
  51.     // std::cout << c1.count()<< std::endl;
  52.     std::vector<int> vec;
  53.     for(int i=0;i<10000000;++i){
  54.         vec.push_back(rand() % 100);
  55.     }
  56.     //没跑一次的时候来计数
  57.     Counter c1;
  58.     double total_value = 0.0f;
  59.     for(auto v : vec){
  60.         total_value += work(v);
  61.         c1.addCount();
  62.     }
  63.     std::cout << "计数器的执行次数=" << c1.count() << "结果=" << total_value << std::endl;

  64.     //开多线程来做同样的工作   三个线程都会去操作的对象:c2和total_value 共享变量
  65.     Counter c2;
  66.     total_value = 0.0f;
  67.     auto iter1 = vec.begin() + (vec.size() / 3);
  68.     auto iter2 = vec.begin() + (vec.size() / 3 * 2);

  69.     //开一个子线程 后台服务  一定要使用共享变量c2
  70.     //问题在这里:线程之间如何保证数据是一致的?
  71.     std::thread service_back([&c2](){
  72.         while (!is_work_finished(c2, 10000000))
  73.         {
  74.             /* 当返回true的时候 该线程才算执行完毕 否则死循环里 */
  75.         }
  76.         
  77.     });
  78.     //一共三个线程  主线程 子线程t1 子线程t2  
  79.     // lambda函数?  知识盲区
  80.     // 引用传进去:c2 total_value
  81.     // 作为值传进去:两个迭代器
  82.     std::thread t1([&c2,&total_value,iter1,iter2](){
  83.         real_work(c2,total_value,iter1,iter2);
  84.     });
  85.     auto end = vec.end();
  86.     std::thread t2([&c2,&total_value,iter2,end](){
  87.         real_work(c2,total_value,iter2,end);
  88.     });


  89.     //主线程里 做第一区间的  
  90.     real_work(c2,total_value,vec.begin(),iter1);

  91.     t1.join();
  92.     t2.join();
  93.     std::cout << "multithread_计数器的执行次数=" << c2.count() << "结果=" << total_value << std::endl;
  94.     service_back.join();
  95.     return 0;
  96. }
复制代码








让天下人人学会人工智能!人工智能的前景一片大好!
回复

使用道具 举报

0

主题

98

帖子

200

积分

中级会员

Rank: 3Rank: 3

积分
200
沙发
发表于 2021-11-23 19:33:32 | 只看该作者
让天下人人学会人工智能!人工智能的前景一片大好!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|人工智能工程师的摇篮 ( 湘ICP备2020019608号-1 )

GMT+8, 2024-5-27 14:24 , Processed in 0.190783 second(s), 18 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表