10.3-chinese
10.3 本章总结本章我们了解了各种与并发相关的bug,从死锁和活锁,再到数据竞争和其他恶性条件竞争;我们也使用了一些技术来定位bug。同样,也讨论了在做代码审阅的时候需做哪些思考,以及写可测试代码的指导意见,还有如何为并发代码构造测试用例。最终,我们还了解了一些对测试很有帮助的工具。
1.0-chinese
第1章 你好,C++的并发世界!本章主要内容 何谓并发和多线程 应用程序为什么要使用并发和多线程 C++的并发史 一个简单的C++多线程程序 令C++用户振奋的时刻到了。距初始的C++标准(1998年)发布13年后,C++标准委员会给语言本身,以及标准库,带来了一次重大的变革。 新C++标准(也被称为C++11或C++0x)在2011年发布,带来一系列的变革让C++编程更加简单和高效。 其中一个最重要的新特性就是对多线程的支持。 C++标准第一次承认多线程在语言中的存在,并在标准库中为多线程提供组件。这意味着使用C++编写与平台无关的多线程程序成为可能,也为可移植性提供了强有力的保证。与此同时,程序员们为提高应用的性能,对并发的关注也是与日俱增,特别在多线程编程方面。 本书是介绍如何使用C++11多线程来编写并发程序,及相关的语言特性和库工具(library facilities)。本书以“解释并发和多线程的含义,为什么要使用并发”作为起始点,在对“什么情况下不使用并发”进行阐述之后,将对C++支持的并发方式进行概述;最后,以一个简单的C++并发实例结束这一章。资深的多线程开...
1.2-chinese
1.2 为什么使用并发?主要原因有两个:关注点分离(SOC)和性能。事实上,它们应该是使用并发的唯一原因;如果你观察得足够仔细,所有因素都可以归结到其中的一个原因(或者可能是两个都有。当然,除了像“就因为我愿意”这样的原因之外)。 1.2.1 为了分离关注点编写软件时,分离关注点是个好主意;通过将相关的代码与无关的代码分离,可以使程序更容易理解和测试,从而减少出错的可能性。即使一些功能区域中的操作需要在同一时刻发生的情况下,依旧可以使用并发分离不同的功能区域;若不显式地使用并发,就得编写一个任务切换框架,或者在操作中主动地调用一段不相关的代码。 考虑一个有用户界面的处理密集型应用——DVD播放程序。这样的应用程序,应具备这两种功能:一,要从光盘中读出数据,对图像和声音进行解码,之后把解码出的信号输出至视频和音频硬件,从而实现DVD的无误播放;二,还需要接受来自用户的输入,当用户单击“暂停”、“返回菜单”或“退出”按键的时候执行对应的操作。当应用是单个线程时,应用需要在回放期间定期检查用户的输入,这就需要把“DVD播放”代码和“用户界面”代码放在一起,以便调用。如果使用多线程方式来...
1.1-chinese
1.1 何谓并发最简单和最基本的并发,是指两个或更多独立的活动同时发生。 并发在生活中随处可见,我们可以一边走路一边说话,也可以两只手同时作不同的动作,还有我们每个人都过着相互独立的生活——当我在游泳的时候,你可以看球赛,等等。 1.1.1 计算机系统中的并发计算机领域的并发指的是在单个系统里同时执行多个独立的任务,而非顺序的进行一些活动。 计算机领域里,并发不是一个新事物:很多年前,一台计算机就能通过多任务操作系统的切换功能,同时运行多个应用程序;高端多处理器服务器在很早就已经实现了真正的并行计算。那“老东西”上有哪些“新东西”能让它在计算机领域越来越流行呢?——真正任务并行,而非一种错觉。 以前,大多数计算机只有一个处理器,具有单个处理单元(processing unit)或核心(core),如今还有很多这样的台式机。这种机器只能在某一时刻执行一个任务,不过它可以每秒进行多次任务切换。通过“这个任务做一会,再切换到别的任务,再做一会儿”的方式,让任务看起来是并行执行的。这种方式称为任务切换。如今,我们仍然将这样的系统称为并发:因为任务切换得太快,以至于无法感觉到任务在何时会被...
1.3-chinese
1.3 C++中的并发和多线程通过多线程为C++并发提供标准化支持是件新鲜事。只有在C++11标准下,才能编写不依赖平台扩展的多线程代码。了解C++线程库中的众多规则前,先来了解一下其发展的历史。 1.3.1 C++多线程历史C++98(1998)标准不承认线程的存在,并且各种语言要素的操作效果都以顺序抽象机的形式编写。不仅如此,内存模型也没有正式定义,所以在C++98标准下,没办法在缺少编译器相关扩展的情况下编写多线程应用程序。 当然,编译器供应商可以自由地向语言添加扩展,添加C语言中流行的多线程API———POSIX标准中的C标准和Microsoft Windows API中的那些———这就使得很多C++编译器供应商通过各种平台相关扩展来支持多线程。这种编译器支持一般受限于只能使用平台相关的C语言API,并且该C++运行库(例如,异常处理机制的代码)能在多线程情况下正常工作。因为编译器和处理器的实际表现很不错了,所以在少数编译器供应商提供正式的多线程感知内存模型之前,程序员们已经编写了大量的C++多线程程序了。 由于不满足于使用平台相关的C语言API来处理多线程,C++程序员...
1.4-chinese
1.4 开始入门ok!现在你有一个能与C++11标准兼容的编译器。接下来呢?一个C++多线程程序是什么样子呢?其实,它看上去和其他C++程序差不多,通常是变量、类以及函数的组合。唯一的区别在于某些函数可以并发运行,所以需要确保共享数据在并发访问时是安全的,详见第3章。当然,为了并发地运行函数,必须使用特定的函数以及对象来管理各个线程。 1.4.1 你好,并发世界从一个经典的例子开始:一个打印“Hello World.”的程序。一个非常简单的在单线程中运行的Hello World程序如下所示,当我们谈到多线程时,它可以作为一个基准。 12345#include <iostream>int main(){ std::cout << "Hello World\n";} 这个程序所做的就是将“Hello World”写进标准输出流。让我们将它与下面清单所示的简单的“Hello, Concurrent World”程序做个比较,它启动了一个独立的线程来显示这个信息。 清单 1.1 一个简单的Hello, Concurre...
1.5-chinese
1.5 本章总结本章中,提及了并发与多线程的含义,以及在你的应用程序中为什么你会选择使用(或不使用)它。还提及了多线程在C++中的发展历程,从1998标准中完全缺乏支持,经历了各种平台相关的扩展,再到新的C++11标准中具有合适的多线程支持。芯片制造商选择了以多核心的形式,使得更多任务可以同时执行的方式来增加处理能力,而不是增加单个核心的执行速度。在这个趋势下,C++多线程来的正是时候,它使得程序员们可以利用新的CPU,带来的更加强大的硬件并发。 使用1.4节中例子,展示C++标准库中的类和函数有多么的简单。C++中使用多线程并不复杂,复杂的是如何设计代码以实现其预期的行为。 尝试了1.4节的示例后,是时候看看更多实质性的内容了。 第2章中,我们将了解一下用于管理线程的类和函数。
2.0-chinese
第2章 线程管理本章主要内容 启动新线程 等待线程与分离线程 线程唯一标识符 好的!看来你已经决定使用多线程了。先做点什么呢?启动线程、结束线程,还是如何监管线程?C++标准库中只需要管理std::thread关联的线程,无需把注意力放在其他方面。不过,标准库太灵活,所以管理起来不会太容易。 本章将从基本开始:启动一个线程,等待这个线程结束,或放在后台运行。再看看怎么给已经启动的线程函数传递参数,以及怎么将一个线程的所有权从当前std::thread对象移交给另一个。最后,再来确定线程数,以及识别特殊线程。
2.1-chinese
2.1 线程管理的基础每个程序至少有一个线程:执行main()函数的线程,其余线程有其各自的入口函数。线程与原始线程(以main()为入口函数的线程)同时运行。如同main()函数执行完会退出一样,当线程执行完入口函数后,线程也会退出。在为一个线程创建了一个std::thread对象后,需要等待这个线程结束;不过,线程需要先进行启动。下面就来启动线程。 2.1.1 启动线程第1章中,线程在std::thread对象创建(为线程指定任务)时启动。最简单的情况下,任务也会很简单,通常是无参数无返回的函数。这种函数在其所属线程上运行,直到函数执行完毕,线程也就结束了。在一些极端情况下,线程运行时,任务中的函数对象需要通过某种通讯机制进行参数的传递,或者执行一系列独立操作;可以通过通讯机制传递信号,让线程停止。线程要做什么,以及什么时候启动,其实都无关紧要。总之,使用C++线程库启动线程,可以归结为构造std::thread对象: 12void do_some_work();std::thread my_thread(do_some_work); 为了让编译器识别std::thread...
2.2-chinese
2.2 向线程函数传递参数清单2.4中,向std::thread构造函数中的可调用对象,或函数传递一个参数很简单。需要注意的是,默认参数要拷贝到线程独立内存中,即使参数是引用的形式,也可以在新线程中进行访问。再来看一个例子: 12void f(int i, std::string const& s);std::thread t(f, 3, "hello"); 代码创建了一个调用f(3, “hello”)的线程。注意,函数f需要一个std::string对象作为第二个参数,但这里使用的是字符串的字面值,也就是char const *类型。之后,在线程的上下文中完成字面值向std::string对象的转化。需要特别要注意,当指向动态变量的指针作为参数传递给线程的情况,代码如下: 12345678void f(int i,std::string const& s);void oops(int some_param){ char buffer[1024]; // 1 sprintf(buffer, "%i",some...













