最近对计算机中并发(concurrency)和并行(parallesim)这两个词的区别很迷惑,将搜索到的相关内容整理如下。

http://www.vaikan.com/docs/Concurrency-is-not-Parallelism/#slide-7

定义:

并发 Concurrency

将相互独立的执行过程综合到一起的编程技术。

并行 Parallelism

同时执行(通常是相关的)计算任务的编程技术。

并发 vs. 并行

并发是指同时处理很多事情。

而并行是指同时能完成很多事情。

两者不同,但相关。

一个重点是组合,一个重点是执行。

并发提供了一种方式让我们能够设计一种方案将问题(非必须的)并行的解决。

并发是一种将一个程序分解成小片段独立执行的程序设计方法。

通信是指各个独立的执行任务间的合作。

这是Go语言采用的模式,包括Erlang等其它语言都是基于这种SCP模式:

C. A. R. Hoare: Communicating Sequential Processes (CACM 1978)

第二种:

https://laike9m.com/blog/huan-zai-yi-huo-bing-fa-he-bing-xing,61/

“并发”指的是程序的结构,“并行”指的是程序运行时的状态

即使不看详细解释,也请记住这句话。下面来具体说说:

并行(parallesim)

这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个“工作单位”在运行就好了。所以,单线程永远无法达到并行状态

要达到并行状态,最简单的就是利用多线程和多进程。但是 Python 的多线程由于存在著名的 GIL,无法让两个线程真正“同时运行”,所以实际上是无法到达并行状态的。

并发(concurrency)

要理解“并发”这个概念,必须得清楚,并发指的是程序的“结构”。当我们说这个程序是并发的,实际上,这句话应当表述成“这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计?

正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行(two tasks can start, run, and complete in overlapping time periods)

这句话的重点有两个。我们先看“(操作)在重叠的时间段内进行”这个概念。它是否就是我们前面说到的并行呢?是,也不是。并行,当然是在重叠的时间段内执行,但是另外一种执行模式,也属于在重叠时间段内进行。这就是协程

使用协程时,程序的执行看起来往往是这个样子:

task1, task2 是两段不同的代码,比如两个函数,其中黑色块代表某段代码正在执行。注意,这里从始至终,在任何一个时间点上都只有一段代码在执行,但是,由于 task1 和 task2 在重叠的时间段内执行,所以这是一个支持并发的设计。与并行不同,单核单线程能支持并发。

并发和并行的关系

Different concurrent designs enable different ways to parallelize.

这句话来自著名的talk: Concurrency is not parallelism。它足够concise,以至于不需要过多解释。但是仅仅引用别人的话总是不太好,所以我再用之前文字的总结来说明:并发设计让并发执行成为可能,而并行是并发执行的一种模式

最后,关于Concurrency is not parallelism这个talk再多说点。自从这个talk出来,直接引爆了一堆讨论并发vs并行的文章,并且无一例外提到这个talk,甚至有的文章直接用它的slide里的图片来说明。

Although there’s a tendency to think that parallelism means multiple cores, modern computers are parallel on many different levels. The reason why individual cores have been able to get faster every year, until recently, is that they’ve been using all those extra transistors predicted by Moore’s law in parallel, both at the bit and at the instruction level.

Bit-Level Parallelism
Why is a 32-bit computer faster than an 8-bit one? Parallelism. If an 8-bit computer wants to add two 32-bit numbers, it has to do it as a sequence of 8-bit operations. By contrast, a 32-bit computer can do it in one step, handling each of the 4 bytes within the 32-bit numbers in parallel. That’s why the history of computing has seen us move from 8- to 16-, 32-, and now 64-bit architectures. The total amount of benefit we’ll see from this kind of parallelism has its limits, though, which is why we’re unlikely to see 128-bit computers soon.

Instruction-Level Parallelism
Modern CPUs are highly parallel, using techniques like pipelining, out-of-order execution, and speculative execution.
As programmers, we’ve mostly been able to ignore this because, despite the fact that the processor has been doing things in parallel under our feet, it’s carefully maintained the illusion that everything is happening sequentially. This illusion is breaking down, however. Processor designers are no longer able to find ways to increase the speed of an individual core. As we move into a multicore world, we need to start worrying about the fact that instructions aren’t handled sequentially. We’ll talk about this more in Memory Visibility, on page ?.

Data Parallelism
Data-parallel (sometimes called SIMD, for “single instruction, multiple data”) architectures are capable of performing the same operations on a large quantity of data in parallel. They’re not suitable for every type of problem, but they can be extremely effective in the right circumstances. One of the applications that’s most amenable to data parallelism is image processing. To increase the brightness of an image, for example, we increase the brightness of each pixel. For this reason, modern GPUs (graphics processing units) have evolved into extremely powerful data-parallel processors.

Task-Level Parallelism
Finally, we reach what most people think of as parallelism—multiple processors. From a programmer’s point of view, the most important distinguishing feature of a multiprocessor architecture is the memory model, specifically whether it’s shared or distributed.

最关键的一点是,计算机在不同层次上都使用了并行技术。之前我讨论的实际上仅限于 Task-Level 这一层,在这一层上,并行无疑是并发的一个子集。但是并行并非并发的子集,因为在 Bit-Level 和 Instruction-Level 上的并行不属于并发——比如引文中举的 32 位计算机执行 32 位数加法的例子,同时处理 4 个字节显然是一种并行,但是它们都属于 32 位加法这一个任务,并不存在多个任务,也就根本没有并发。

所以,正确的说法是这样:
并行指物理上同时执行,并发指能够让多个任务在逻辑上交织执行的程序设计

按照我现在的理解,并发针对的是 Task-Level 及更高层,并行则不限。这也是它们的区别。

并发与并行(concurrency vs parallesim)的更多相关文章

  1. [更新中]并发和并行(Concurrency and Parallelism)

    书籍的简称: CSPPSE: Computer System: a programmer's perspective Second Edition 术语并发是一个通用的概念, 指同时具有多个活动的系统 ...

  2. 并发与并行的区别 The differences between Concurrency and Parallel

    逻辑控制流 在程序加载到内存并执行的时候(进程),操作系统会通过让它和其他进程分时段占用CPU(CPU slices)让它产生自己独占CPU的假象(同时通过虚拟内存让它产生独占内存的假象).在CPU在 ...

  3. Python 多线程教程:并发与并行

    转载于: https://my.oschina.net/leejun2005/blog/398826 在批评Python的讨论中,常常说起Python多线程是多么的难用.还有人对 global int ...

  4. java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器

    多线程并发就像是内功,框架都像是外功,内功不足,外功也难得精要. 1.进程和线程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 用工厂来比喻就是,一个工厂可以生产不同种类的产品,操作系统就是 ...

  5. Go语言并发与并行学习笔记(三)

    转:http://blog.csdn.net/kjfcpua/article/details/18265475 Go语言并发的设计模式和应用场景 以下设计模式和应用场景来自Google IO上的关于G ...

  6. [CSAPP]并发与并行

    学了这么久的计算机,并发与并行的概念理解的一直不够透彻.考研复习那会儿,以为自己懂了,然而直到看了CSAPP才算是真正明白了这俩个概念. 并发(concurrency) 流X和流Y并发运行是指,流X在 ...

  7. Golang 入门 : 理解并发与并行

    Golang 的语法和运行时直接内置了对并发的支持.Golang 里的并发指的是能让某个函数独立于其他函数运行的能力.当一个函数创建为 goroutine 时,Golang 会将其视为一个独立的工作单 ...

  8. Java多线程专题1: 并发与并行的基础概念

    合集目录 Java多线程专题1: 并发与并行的基础概念 什么是多线程并发和并行? 并发: Concurrency 特指单核可以处理多任务, 这种机制主要实现于操作系统层面, 用于充分利用单CPU的性能 ...

  9. geotrellis使用(六)Scala并发(并行)编程

    本文主要讲解Scala的并发(并行)编程,那么为什么题目概称geotrellis使用(六)呢,主要因为本系列讲解如何使用Geotrellis,具体前几篇博文已经介绍过了.我觉得干任何一件事情基础很重要 ...

随机推荐

  1. RocketMQ学习笔记(13)----RocketMQ的Consumer消息重试

    1. 概念 Producer端重试: 生产者端的消息失败,也就是Producer往MQ上发消息没有发送成功,比如网络抖动导致生产者发送消息到MQ失败. 这种消息失败重试我们可以手动设置发送失败重试的次 ...

  2. BZOJ 1058: [ZJOI2007]报表统计 multiset + 卡常

    Code: #include<bits/stdc++.h> #define maxn 600000 #define inf 1000000000 using namespace std; ...

  3. Tensorflow学习笔记(1):tf.slice()函数使用

    tensorflow 当中的一个常用函数:Slice() def slice(input_, begin, size, name=None) 函数的功能是根据begin和size指定获取input的部 ...

  4. (七)python3 切片

    切片:取一个 list 或 tuple 的部分元素是非常常见的操作 >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] #笨办法 ...

  5. 设置Python解析器

    如果同时安装了多个Python,如 Python2.7 和 Python3.7 .如果某些特殊原因(比如有些框架只能在Python2.7中使用),需要修改程序在 Python2.7 下运行,即可设置P ...

  6. 调用subprocess调用shell命令时屏蔽标准输出

    import os, subprocessp = subprocess.Popen(args, stdout = subprocess.PIPE,stderr = subprocess.STDOUT)

  7. pxc增量恢复

    解流 xbstream -x < fullbackup2.xbstream -C /data/fullbackup xbstream -x < fullbackup2.xbstream - ...

  8. foreach获取访问元素的下标

    今天在用foreach循环的时候,要同时根据访问下标获取另一个list对象数据,之前想的方法是加一个变量i,然后每次i++,但是感觉这样不是很好!,后来发现这样也可以!举个简单的例子! foreach ...

  9. 关于构造函数及参数执行顺序说明(c#)

    原文:https://blog.csdn.net/junmail/article/details/83249186 构造函数的执行顺序: 子类静态变量>子类静态构造函数>子类非静态变量&g ...

  10. 关于zookeeper和zkfc的一些测试

    1.停掉zookeeper集群 ****进程影响****** zkfc:报错无法连接zookeeper.ClientCnxn java.net.connectexception:拒绝连接,但不会shu ...