14.十面埋伏的并发:多线程真的很难吗<罗剑锋的C++实战笔记 >

14.十面埋伏的并发:多线程真的很难吗

并发与多线程

并发有很多实现方式,而多线程只是其中最常用的手段

认识多线程

在C++中,线程就是一个能够独立运行的函数.你可以写一个lambda表达式让它在多线程中跑起来

  • auto f = // 定义一个lambda表达式
    {
    cout << "tid=" <<
    this_thread::get_id() << endl;
    };
    thread t(f); // 启动一个线程,运行函数f

好处:任务并行,避免IO阻塞,充分利用CPU,提高用户界面响应速度

基本常识:读而不写就不会有数据竞争

  • 读取const变量,对类调用const成员函数,
    对容器调用只读算法都是线程安全的

开发原则:最好的并发就是没有并发,最好的多线程就是没有线程

  • 在微观层面"看不到"线程,减少死锁,同步的恶性问题出现

多线程开发实践

基本工具:仅调用一次,线程局部存储,原子变量和线程对象

1.仅调用一次

  • 让初始化操作只调用一次,防止多线程没有同步时调用多次
  • 声明once_flag类的变量,最好是静态,全局的(线程可见);然后利用call_once()来进行调用
  • auto f = // 在线程里运行的lambda表达式
    {
    std::call_once(flag, // 仅一次调用,注意要传flag
    { // 匿名lambda,初始化函数,只会执行一次
    cout << "only once" << endl;
    } // 匿名lambda结束
    ); // 在线程里运行的lambda表达式结束
    };
    thread t1(f); // 启动两个线程,运行函数f
    thread t2(f);

2.线程局部存储

  • 读写全局(或局部静态)变量是常见的数据竞争场景

  • 有时候全局变量并不一定是必须共享的,
    可能是为了方便线程传入传出数据

    • 这种情况就是线程独占所有权,即线程局部存储
  • 有thread_local(和static,extern同级的关键字)标记的变量
    在每个线程里都会有一个独立的副本,是线程独占的

    • thread_local int n = 0;
      auto f2 = [&](int x) {
      cout << n << endl; //0
      n += x;
      cout << n << endl; //10 或者 100
      };
      thread t3(f2, 10);
      thread t4(f2, 100);
      t3.join();
      t4.join();
      cout << n << endl; //此处的n是主线程中的n,因此仍然是0.
    • 而如果把例子中的thread_local改为static,
      则变成所有的线程共享了

3.原子变量

  • 原子指在线程领域中不可分,操作要么完成,要么未完成.
  • 互斥量成本太高,对于小数据,原子化(atomic)是更好的方案
  • C++只能让一些基本的类型原子化,比如atomic_int,atomic_long等
  • 原子变量禁用了拷贝构造函数,因此初始化时不能用=,需要用()或{}
  • 最基本用法是作为线程安全的全局计数器或者标志位;别的用法是实现无锁数据结构

4.线程对象

  • 上面三个工具,都不与线程直接相关,但可以用于多线程编程,消除显式使用线程

  • std::thread成员与方法

    • 变量

      • id
    • 观察器

      • get_id, joinable
    • 操作

      • join,detach,swap
  • std::this_thread方法:yield,get_id,sleep_for,sleep_until

async()

  • 异步运行一个任务,隐含动作是启动一个线程去执行

    • 不绝对保证立即启动,可在第一参数传递std::launch::async,立即启动现场
  • 隐蔽的"坑"

    • 不显式获取async()的返回值,就会同步阻塞值任务完成
    • std::async(task, ...); // 没有显式获取future,被同步阻塞
      auto f = std::async(task, ...); // 只有上一个任务完成后才能被执行

XMind - Trial Version

新战场:https://blog.csdn.net/Stephen___Qin
原文地址:https://www.cnblogs.com/Stephen-Qin/p/13058217.html