[关闭]
@lancelot-vim 2016-07-08T02:44:02.000000Z 字数 8822 阅读 1704

第一章 并发编程基础

c++并行编程指南


本章主要内容如下:

我会在本章介绍一些最基本的概念,关键词如:程序,并发,并行,进程,多线程,进程间通信,线程间数据共享问题,通过对本章内容的学习,你会对并发编程有一个大致的认识,究竟什么是并发编程,并发和并行的联系和区别,什么场景下需要使用并发编程以及并发的优势和缺陷等。对于初学者,我会给出几个经典的示例激发大家的兴趣。

引言

C++11 是 2011 年 9 月 1 号发布的。C++11 在 C++03 的基础上做了大量的改进,引入了很多新的特性,比如 Lambda 表达式,右值引用,统一的列表初始化方式,正则表达式等等。当然,其中最令人激动的特性是新标准引入了原子操作类和线程支持库。C++ 一直在语言层面缺少对多线程的支持,因此 C++11 新标准基本上弥补了这一缺陷。可以毫不夸张地说,C++11 相当于一门新的编程语言。

相信 Linux 程序员都用过 Pthread, 但有了 C++11 的 std::thread 以后,你可以在语言层面编写多线程程序了,直接的好处就是多线程程序的可移植性得到了很大的提高,所以作为一名 C++ 程序员,熟悉 C++11 的多线程编程方式还是很有益处的。

如果你对 C++11 不太熟悉,建议先看看维基百科上关于 C++11 新特性的介绍,中文C++11介绍,英文C++11介绍 ,另外C++之父 Bjarne Stroustrup 的关于 C++11 的 FAQ 也是必看的

与 C++11 多线程相关的头文件

C++11 新标准中引入了五个头文件来支持多线程编程,它们分别是 <atomic>, <thread>, <mutex>, <condition_variable><future>

std::thread "Hello world"

下面是一个最简单的使用 std::thread 类的例子

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread

void thread_task() {
    std::cout << "hello thread" << std::endl;
}

int main(int argc, const char *argv[])
{
    std::thread t(thread_task);
    t.join();

    return EXIT_SUCCESS;
}

Makefile 如下:

all:Thread

CC=g++
CPPFLAGS=-Wall -std=c++11 -ggdb
LDFLAGS=-pthread

Thread:Thread.o
    $(CC) $(LDFLAGS) -o $@ $^

Thread.o:Thread.cc
    $(CC) $(CPPFLAGS) -o $@ -c $^


.PHONY:
    clean

clean:
    rm Thread.o Thread

注意在 Linux GCC4.6 环境下,编译时需要加 -pthread,否则执行时会出现:

$ ./Thread
terminate called after throwing an instance of 'std::system_error'
  what():  Operation not permitted
Aborted (core dumped)

原因是 GCC 默认没有加载 pthread 库,据说在后续的版本中可以不用在编译时添加 -pthread 选项。
如果 -std=c++11 不被支持,请尝试使用 -std=c++0x 代替并确保GCC已经升级。
更多的有关 C++11 Concurrency 的介绍将在后续的一系列博客中写出,希望自己勤快一点吧 ;-)


1.1 什么是并发

并发(Concurrency)在我们的现实世界中随处可见,以至于我们常常忽略了它的存在,比方说你在工作(假设你是一名程序员,你的工作就是编程)的时候也可以听听自己喜欢的音乐,并且你的耳朵并不会因为手头的工作而忽略了声音的存在(当然,除非你自己有意的去忽略它,但你还是能够听得见声音,只是你的大脑可能不会去感受音乐的节奏),此时你的大脑既要控制你的双手敲击键盘,也要控制你的耳朵去感受音乐。因此,在一定程度上,你的大脑就在并发地处理不同的事情,并且每个时刻都可能会侧重处理某件事情,比如某个时刻音乐达到高潮并且是你喜欢的旋律,你可能会放慢或者停止手边的工作,但在另外一个时刻你正在编写关键代码,需要全神贯注来避免 Bug 的出现,你可能会把声音调小一点或者干脆摘掉耳机。所以,我们的大脑就在并发地指导我们完成各种任务,或者换一种说法,我们需要处理的任务并发地征用我们的大脑,大脑就相当于计算机的 CPU,而待处理的任务就相当于计算机程序(更确切地说应该是进程或线程等执行实体)。

不过在现实世界中,我们并不会严格定义什么是并发。而在计算机程序世界中,为了编写高性能的代码,我们应该理解什么是并发,并发的基本特性是什么,哪些问题可以使用并发编程来(高效地)解决,哪些情况下又应该尽量避免使用并发编程,我们在使用并发编程时需要注意一些什么问题,本章的将会给大家介绍并发的基本概念,带领大家学习并发编程的基本技巧。

1.2 并发与并行的联系和区别

与并发相近的另一个概念是并行(Parallel)。和并发所描述的情况一样,并行也是指两个或多个任务被同时执行。但是严格来讲,并发和并行的概念并是不等同的,两者存在很大的差别。下面我们来看看计算机科学家们是怎么区分并发和并行的。

1.2.1 Erlang 之父 Joe Armstrong 的观点

Erlang 是一种通用的并行程序设计语言,在并行、分布式和容错等方面表现优异。下面是 Erlang 官方的介绍:

Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Erlang's runtime system has built-in support for concurrency, distribution and fault tolerance.

Erlang 的发明者 Joe Armstrong 在他的一篇博文(原文链接)中提到如何向一个 5 岁的小孩去介绍并发和并行的区别,并给出了下面一幅图(下图是自己重绘的,原图连接):

并发与并行的区别

直观来讲,并发是两个等待队列中的人同时去竞争一台咖啡机(当然,人是有理性懂礼貌的动物(也不排除某些很霸道的人插队的可能),两队列中的排队者也可能约定交替使用咖啡机,也可能是大家同时竞争咖啡机,谁先竞争到咖啡机谁使用,不过后一种的方法可能引发冲突,因为两个队列里面排在队列首位的人可能同时使用咖啡机),每个等待者在使用咖啡机之前不仅需要知道排在他前面那个人是否已经使用完了咖啡机,还需知道另一个队列中排在首位的人是否也正准备使用咖啡机;而并行是每个队列拥有自己的咖啡机,两个队列之间并没有竞争的关系,队列中的某个排队者只需等待队列前面的人使用完咖啡机,然后再轮到自己使用咖啡机。

因此,并发意味着多个执行实体(比方说上面例子中的人)可能需要竞争资源(咖啡机),因此就不可避免带来竞争和同步的问题;而并行则是不同的执行实体拥有各自的资源,相互之间可能互不干扰。

1.2.2. Go 发明者之一 Rob Pike 的观点

Go 是一门新兴的编程语言,Go 官方对其介绍如下:

The Go programming language is an open source project to make programmers more productive.

Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines.

Go 的并行机制使其非常容易编写多核和网络应用。Go 语言的并发模型基于 CSP(Communicating sequential processes, 参见维基百科 CSP)。Go 提供了 goroutines(并发执行), channels(同步和通信), select(多路并发控制) 等特性来支持并发编程。Go 的发明者之一 Rob Pick 在他的一篇讲稿(Concurrency is not Parallelism(it's better))中提到:

Concurrency: Programming as the composition of independently executing processes.

Parallelism: Programming as the simultaneous execution of (possibly related) computations.

Rob 认为并发是程序本身的一种特性,程序被分为多个可独立执行的部分,而各个可独立执行的片段通过通信手段进行协调(后文会提到),而并行则是程序的计算过程(不同的计算过程可能相关联)同时执行。

Rob Pike 的观点是: 并发是一次处理(dealing with)很多事情,而并行是一次做(doing)很多事情.(注: 英文词汇的表达也很微妙)原文是如下:

Concurrency is about dealing with lots of things at once.

Parallelism is about doing lots of things at once.

前者是关于程序结构的,而后者是关于程序执行的。Rob 认为:

Concurrency provides a way to structure a solution to solve a problem that may (but not necessarily) be parallelizable.

即我们可以利用并发的手段去构建一种解决方案来解决那些有可能被并行处理的问题。

作者在本文中还提到,设计并发程序时应该将程序分为多个执行片段,使得每个片段可以独立执行。不同执行片段通过通信(Communication )来进行协调。因此 Go 的并发模型基于 CSP: C. A. R. Hoare: Communicating Sequential Processes (CACM 1978)

作者后面还给出了一个例子来阐述他的观点,感兴趣的读者可以继续阅读:(Concurrency is not Parallelism(it's better))

1.2.3 Haskell 语言中的并发与并行

(本小节暂未完成)

1.2.4 其他观点

另外,Intel 中文网站的一篇文章(原文链接)曾这样写道(可能不是很权威,不过可以大致说明并发与并行的区别),

并发(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是单个物理 CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发可以对有限物理资源强制行使多用户共享以提高效率,如下图所示:

并发

并行(Parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行,如下图所示:

并行

因此,该文认为并发与并行的区别是:并发是一个处理器同时处理多个任务,而并行多个处理器或者是多核的处理器同时处理多个不同的任务。前者是逻辑上的同时发生(simultaneous),而后者是物理上的同时发生。

而两者的联系是:并行的事件或活动一定是并发的,但反之并发的事件或活动未必是并行的。并行性是并发性的特例,而并发性是并行性的扩展(个人不赞同此观点)。

1.2.5 小节

本文主要讲了什么是并发以及并发和并行的联系和区别。总得来说,Joe Armstrong 的观点通俗易懂,Rob Pike 有关并发和并行的的观点也很有意思。而关于并发和并行具体的差异,本文最后介绍了一种教科书式的解释。读者可以根据自己的理解来选择认同上述某一种或几种观点。


资料汇

http://www.open-std.org/jtc1/sc22/wg21/

C++0x/C++11 Support in GCC:http://gcc.gnu.org/projects/cxx0x.html

What is C++0x:https://www2.research.att.com/~bs/what-is-2009.pdf

Overview of the New C++:http://www.artima.com/shop/overview_of_the_new_cpp

Overview of the New C++ (C++0x).pdf:http://ishare.iask.sina.com.cn/f/20120005.html?from=like

A Brief Look at C++0x:http://www.artima.com/cppsource/cpp0x.html

Summary of C++11 Feature Availability in gcc and MSVC:http://www.aristeia.com/C++11/C++11FeatureAvailability.htm

C++ 11: Come Closer:http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer

C++11 threads, locks and condition variables: http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables

Move Semantics and Perfect Forwarding in C++11:http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus

http://solarianprogrammer.com/categories/C++11/

C++11 Concurrency:http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/

http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf

http://en.cppreference.com/w/cpp/thread

http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov

The Biggest Changes in C++11:http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/

Ten C++11 Features Every C++ Developer Should Use:http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer

C++11 – A Glance [part 1 of n]:http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n

C++11 – A Glance [part 2 of n]:http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n

C++11(及现代C++风格)和快速迭代式开发:http://mindhacks.cn/2012/08/27/modern-cpp-practices/

Lambda Functions in C++11 - the Definitive Guide:http://www.cprogramming.com/c++11/c++11-lambda-closures.html

Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html

Rvalue-references-and-move-semantics-in-c++11:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html

http://www.gotw.ca/publications/index.htm

http://www.devx.com/SpecialReports/Door/38865

Multi-threading in C++0x:http://accu.org/index.php/journals/1584

C++ 0X feature summary cheat sheat:http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/

Multithreading in C++0x part 1: Starting Threads:http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html

http://en.cppreference.com/w/cpp/thread

http://www.cplusplus.com/reference/multithreading/

Parallelism is not concurrency: http://existentialtype.wordpress.com/2011/03/17/parallelism-is-not-concurrency/

Concurrent and Parallel Programming: http://joearms.github.io/2013/04/05/concurrent-and-parallel-programming.html

Parallelism /= Concurrency: http://ghcmutterings.wordpress.com/2009/10/06/parallelism-concurrency/

Parallelism vs. Concurrency: http://www.haskell.org/haskellwiki/Parallelism_vs._Concurrency

Concurrency is not Parallelism: http://concur.rspace.googlecode.com/hg/talk/concur.html#title-slide

Concurrency vs Parallelism - What is the difference?: http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference

Difference between concurrent programming and parallel programming: http://stackoverflow.com/questions/1897993/difference-between-concurrent-programming-and-parallel-programming?rq=1

Parallelism and concurrency need different tools: http://www.yosefk.com/blog/parallelism-and-concurrency-need-different-tools.html

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注