WHCSRL 技术网

多线程mutex_qq

1.线程不锁,导致最终获取数据不对

  1. int sum = 0;
  2. mutex mu;
  3. void A()
  4. {
  5. for (int i=0;i<100000;++i)
  6. {
  7. sum++;
  8. }
  9. cout << "a is :" << sum << endl;
  10. }
  11. int main()
  12. {
  13. thread a(A);
  14. thread b(A);
  15. thread c(A);
  16. a.join();
  17. b.join();
  18. c.join();
  19. cout << "父线程" << endl;
  20. cout << "a is :" << sum << endl;
  21. return 0;
  22. }

 2.线程加mutex锁,方法:对数据采用lock()和unlock()方法成对出现

  1. void A()
  2. {
  3. for (int i=0;i<100000;++i)
  4. {
  5. mu.lock();
  6. sum++;
  7. mu.unlock();
  8. }
  9. cout << "a is :" << sum << endl;
  10. }

3.lock()和unlock(),有两个问题:

1.写两个太麻烦

2.如果在lock过程抛出异常会导致死锁

采用的方法是用lock_guard<mutex>,它会自己解锁。在某个lock_guard对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而lock_guard的生命周期结束之后,它所管理的锁对象会被解锁。

  1. void A()
  2. {
  3. for (int i=0;i<100000;++i)
  4. {
  5. lock_guard<mutex> lockmu(mu);
  6. sum++;
  7. }
  8. cout << "a is :" << sum << endl;
  9. }

 4.当有两个锁锁数据的时候,如果调用顺序不同会产生死锁,在A中顺序是a,b,在B中顺序是b,a

会导致程序死锁。

  1. int sum = 0;
  2. mutex mu;
  3. mutex mu2;
  4. void A()
  5. {
  6. for (int i=0;i<100000;++i)
  7. {
  8. lock_guard<mutex> lockmu(mu);
  9. sum++;
  10. lock_guard<mutex> lockmu2(mu2);
  11. sum++;
  12. }
  13. cout << "a is :" << sum << endl;
  14. }
  15. void B()
  16. {
  17. for (int i = 0; i < 100000; ++i)
  18. {
  19. lock_guard<mutex> lockmu2(mu2);
  20. sum++;
  21. lock_guard<mutex> lockmu(mu);
  22. sum++;
  23. }
  24. cout << "a is :" << sum << endl;
  25. }
  26. int main()
  27. {
  28. thread a(A);
  29. thread b(B);
  30. a.join();
  31. b.join();
  32. cout << "父线程" << endl;
  33. cout << "a is :" << sum << endl;
  34. return 0;
  35. }

5.解决的办法是采用lock开始就规定好锁的顺序,并在每个锁中加入adopt_lock适配lock的参数,可以保证锁的顺序。

  1. int sum = 0;
  2. mutex mu;
  3. mutex mu2;
  4. void A()
  5. {
  6. for (int i=0;i<100000;++i)
  7. {
  8. lock(mu, mu2);
  9. lock_guard<mutex> lockmu(mu,adopt_lock);
  10. sum++;
  11. lock_guard<mutex> lockmu2(mu2, adopt_lock);
  12. sum++;
  13. }
  14. cout << "a is :" << sum << endl;
  15. }
  16. void B()
  17. {
  18. for (int i = 0; i < 100000; ++i)
  19. {
  20. lock(mu, mu2);
  21. lock_guard<mutex> lockmu2(mu2, adopt_lock);
  22. sum++;
  23. lock_guard<mutex> lockmu(mu, adopt_lock);
  24. sum++;
  25. }
  26. cout << "a is :" << sum << endl;
  27. }
  28. int main()
  29. {
  30. thread a(A);
  31. thread b(B);
  32. a.join();
  33. b.join();
  34. cout << "父线程" << endl;
  35. cout << "a is :" << sum << endl;
  36. return 0;
  37. }

 6.和lock_guard相对的还有一个更灵活的锁unique_lock,

unique_lock优点:

1.在lock_guard的范围下,所有代码都被锁住,而unique_lock范围下,如果不加参数(defer_lock),效果如下:

 2.unique_lock可以通过move函数转移控制权,而lock_guard不行。

unique_lock缺点:

消耗更多性能。

7.多线程的call_once的使用

转载:C++11中的std::call_once_爱很遥远-CSDN博客

8.条件变量condition_variable使用

主要要点:在于相当于在全局设置了一个记录器,被触发一次,就会记录一次,然后要用的时候就减一次。

转载:C++-----------notify_one()与notify_all()_1.01的博客-CSDN博客

  1. condition_variable cv;
  2. mutex mu;
  3. vector<int> sum;
  4. void A()
  5. {
  6. for (int i = 0; i < 10; ++i)
  7. {
  8. unique_lock<mutex> locker(mu);
  9. cv.notify_one();
  10. sum.emplace_back(i);
  11. cout << "sum is:" << i << endl;
  12. }
  13. }
  14. void B()
  15. {
  16. int data = 0;
  17. while (data!=9)
  18. {
  19. unique_lock<mutex> locker(mu);
  20. cv.wait(locker, []() {return sum.size()!=0; });
  21. data = sum[0];
  22. sum.erase(sum.begin());
  23. cout << "data is:" << data << endl;
  24. }
  25. }
  26. int main()
  27. {
  28. thread a(A);
  29. thread b(B);
  30. a.join();
  31. b.join();
  32. cout << "父线程" << endl;
  33. return 0;
  34. }

9.异步线程future和 async、promise

1.std::promise 是C++11并发编程中常用的一个类,常配合std::future使用

在两个线程中,promise  p,future f=p.get_future, 两个线程同时启动,当遇到f.get(),如果另一个线程不给p设置值,f就会一直阻塞,等待,线程p中被设置了值,才继续下去

多线程笔记一 future和async的使用_qq_1410888563的博客-CSDN博客

  1. void A(promise<int> &p)
  2. {
  3. cout << "1:" << endl;
  4. p.set_value(10);
  5. cout << "3:" << endl;
  6. int a = 20;
  7. a = a * 10;
  8. cout << "data is:" <<a << endl;
  9. }
  10. void B(future<int>& f)
  11. {
  12. cout << "2:" << endl;
  13. int data =f.get();
  14. cout << "data is:" << data <<endl;
  15. }
  16. int main()
  17. {
  18. promise<int> pro;
  19. future<int> fu = pro.get_future();
  20. thread a(A,ref(pro));
  21. thread b(B,ref(fu));
  22. a.join();
  23. b.join();
  24. cout << "父线程" << endl;
  25. return 0;
  26. }

10.packaged-task

主要作用:使代码更加简洁

  1. int A(int a, int& b)
  2. {
  3. b += 10;
  4. return a + b;
  5. }
  6. int main()
  7. {
  8. packaged_task<int(int, int&)> t(A);
  9. future<int> f = t.get_future();
  10. int num = 10;
  11. thread a(move(t),10, ref(num));
  12. a.join();
  13. int b= f.get();
  14. cout << "b is :" <<b<< endl;
  15. cout << "num is :" << num << endl;
  16. return 0;
  17. }

 11.时间限制4种类型方法

  1. int main()
  2. {
  3. thread t(A);
  4. this_thread::sleep_for(chrono::milliseconds(3));
  5. chrono::steady_clock::time_point tp = chrono::steady_clock::now() + chrono::microseconds(4);
  6. this_thread::sleep_until(tp);
  7. mutex mu;
  8. unique_lock<mutex> locker(mu);
  9. locker.try_lock_for(chrono::microseconds(3));
  10. locker.try_lock_until(tp);
  11. condition_variable cond;
  12. cond.wait_for(locker,chrono::microseconds(3));
  13. cond.wait_until(locker, tp);
  14. promise<int> p;
  15. future<int> f = p.get_future();
  16. f.wait_for(chrono::microseconds(3));
  17. f.wait_until(tp);
  18. return 0;
  19. }

推荐阅读