罗剑锋的 C++ 实战笔记
罗剑锋
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
35514 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 32 讲
结束语 (1讲)
罗剑锋的 C++ 实战笔记
15
15
1.0x
00:00/00:00
登录|注册

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

async()
thread
atomic
thread_local
call_once()
主线程和子线程
C++语言中的线程
线程
原子变量
线程局部存储
仅调用一次
多线程的挑战
多线程的好处
线程概念
多线程开发实践
认识线程和多线程
C++20的协程
async()
atomic
thread_local
call_once()
多线程编程的挑战
多线程
课下作业
小结
并发
文章

该思维导图由 AI 生成,仅供参考

你好,我是 Chrono。
今天,我们来聊聊“并发”(Concurrency)、“多线程”(multithreading)。
在 20 年前,大多数人(当然也包括我)对这两个词还是十分陌生的。那个时候,CPU 的性能不高,要做的事情也比较少,没什么并发的需求,简单的单进程、单线程就能够解决大多数问题。
但到了现在,计算机硬件飞速发展,不仅主频上 G,还有了多核心,运算能力大幅度提升,只使用单线程很难“喂饱”CPU。而且,随着互联网、大数据、音频视频处理等新需求的不断涌现,运算量也越来越大。这些软硬件上的变化迫使“并发”“多线程”成为了每个技术人都不得不面对的课题。
通俗地说,“并发”是指在一个时间段里有多个操作在同时进行,与“多线程”并不是一回事。
并发有很多种实现方式,而多线程只是其中最常用的一种手段。不过,因为多线程已经有了很多年的实际应用,也有很多研究成果、应用模式和成熟的软硬件支持,所以,对这两者的区分一般也不太严格,下面我主要来谈多线程。

认识线程和多线程

要掌握多线程,就要先了解线程(thread)。
线程的概念可以分成好几个层次,从 CPU、操作系统等不同的角度看,它的定义也不同。今天,我们单从语言的角度来看线程。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

多线程编程是当前技术人员必须面对的重要课题。本文从C++语言的角度出发,介绍了线程和多线程的概念,以及多线程开发的实践。文章强调了多线程编程的挑战和重要性,提出了“读而不写”避免数据竞争的原则,并介绍了C++标准库提供的工具,如call_once()和thread_local,来改善多线程应用。此外,文章还介绍了原子变量的概念和使用方法,以及C++标准库提供的线程类thread和异步任务函数async()的使用。总的来说,本文通过介绍C++中的多线程编程工具和技术,为读者提供了更好地规划多线程任务、减少并发冲突、提高多线程编程效率和安全性的方法。文章还展望了C++20加入的协程技术,为读者展示了未来并发编程的发展方向。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《罗剑锋的 C++ 实战笔记》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(37)

  • 最新
  • 精选
  • 泡泡龙
    虽然没有C++的多线程经验,但是记得其他语言的多线程,用IDE打断点调试是一个极难的问题

    作者回复: 多线程调试从来都不是个简单的工作,一般都是打日志,里面列出线程号和使用的变量,然后再慢慢分析。

    2020-06-07
    2
    21
  • 战斗机二虎🐯
    nginx的自旋锁实现就是一个非常成功的工程级的自旋锁

    作者回复: 对,Nginx的自旋锁代码非常精致,值得学习。

    2020-07-06
    20
  • 青鸟飞鱼
    老师你好,假如一个变量用volatile修饰,是不是就可以不用加锁?

    作者回复: 这种想法是非常危险的,一定要特别注意。 volatile只是表示变量易变,与线程没有任何关系。 不加锁应该用atomic变量。

    2020-06-07
    9
    12
  • 风清扬
    async那里隐含的坑可以结合如下代码验证: ``` auto task = [](auto x) { //using namespace std::chrono_literals; this_thread::sleep_for( x * 1ms); cout << "sleep for " << x << endl; return x; }; //先输出Hello,world auto f = std::async(task, 100); cout << "Hello,world" << endl; #if 0 //先输出sleep for 100 std::async(task, 100); cout << "Hello,world" << endl; #endif return 0; ```

    作者回复: 很好的例子,赞。

    2020-11-08
    6
  • 范闲
    C++内怎么做异步呢? 1. Callback 2.多线程+Callback 但是这两个都有个问题,callback也是会阻塞的。如果有A B C D四个流程,B C D分别依赖于前一个的输出,这种callback就会调用栈太深,容易爆栈。 最近对异步编程模式产生了些疑问,应该怎么解决?

    作者回复: callback并不能实现异步,但异步多用callback来实现解耦回调,比如actor和proactor这两种异步模式。 我理解的阻塞有两种,一种是io阻塞,一种是cpu计算量阻塞,前者可以用异步回调来避免,而后者就只能用多线程了。 目前在C++里,异步编程主要还是用多线程,而callback难于使用和理解,把处理流程分割的支离破碎,以后可能会用协程更好。

    2020-07-31
    5
  • 二杠一
    老师,在多个线程里同时往输出流输出,怎么保证一个线程的单次输出操作是完整的 ? 比如"std::cout << x << std::endl;"怎么保证不会漏掉"std::endl"这个输出,只能加锁吗?

    作者回复: 在C++11/14里好像只能这样,在C++20里好像多了一个新的cout对象,是线程安全的。 不过我不太建议在多线程里用cout,一是不安全,二是也没什么意义,改成写日志文件的方式可能会更好。

    2020-06-11
    3
    5
  • robonix
    (比如内部使用线程池或者其他机制)。那么async怎么跟线程池结合呢?老师有没有一些好的参考资料提供哈

    作者回复: async内部可能有优化,注意,只是可能,要看编译器具体怎么做,也可能不会优化,而且我们也无法控制他。 想要用线程池,还是得自己写,不过其实也比较简单,创建多个线程用list容纳就可以了。 可以参考boost.thread_pool,它就是这么做的。

    2020-06-15
    4
  • 被讨厌的勇气
    当需要获取线程的结果时,使用async可以直接获取其结果;使用thread则需要通过共享数据来获取,需要使用锁、条件变量。 async的缺点是只能获取一次。若需要保证线程一直运行,多次获取其‘结果’时,只能使用thread + condition_variable,不知道这样理解对不对? thread_local的应用场景问题。若不需要共享数据,直接在lambad表达式中的捕获列表中进行值捕获不就每个线程一个副本了,没想出来必须使用thread_local的应用场景。

    作者回复: 1.基本正确,async()是一种相当简化的线程用法,目的就是获取future值,如果不是这个场景就不如thread灵活。 2.比如说线程里要保存一些cache数据,很显然这些cache不会是多线程共享的,用thread_local就比较好。可以把它理解成是专门给线程准备的static全局变量。

    2020-06-06
    2
    4
  • Stephen
    子线程会立即脱离主线程的控制流程,单独运行,但共享主线程的数据。这里指的是全局变量吧,像局部变量应该不行吧?

    作者回复: 也不一定,看传给子线程什么参数了,局部变量也可以给子线程共享。

    2020-06-06
    5
    3
  • dog_brother
    老师,我们生产环境是c++ 11,但是我们几乎不使用std的thread,主要是pthread系列函数。听说是因为std thread的坑比较多。

    作者回复: 这个还是看怎么用吧,仔细看它的api,真正有哪些坑只有自己实际使用了才能知道,也许不会遇到那些坑。 thread使用起来很方便,其实就是对操作系统线程接口的封装,可以先小范围使用,再逐步替换pthread。

    2021-04-24
    2
收起评论
显示
设置
留言
37
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部