zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    EditPlus保存文件时不生成其备份文件的方法
    一台电脑同时运行多个tomcat配置方法
    Dom4j写XML
    .....
    编程备忘录
    背包问题
    chrome新版不再支持-webkit-text-size-adjust
    安装grunt需要的grunt插件
    初学web前端
    心情烦躁、、
  • 原文地址:https://www.cnblogs.com/Stephen-Qin/p/13058217.html
Copyright © 2011-2022 走看看