并行编程和并行编程的区别是什么?我问了谷歌,但没有找到任何帮助我理解这种区别的东西。你能给我举个例子吗?

现在我找到了这个解释:http://www.linux-mag.com/id/7411 -但是“并发性是程序的属性”vs“并行执行是机器的属性”对我来说还不够-我仍然不能说什么是什么。


我认为并发编程指的是多线程编程,它是关于让你的程序运行多个线程,从硬件细节中抽象出来。

并行编程是指专门设计程序算法以利用可用的并行执行。例如,您可以并行执行某些算法的两个分支,期望它会比先检查第一个分支再检查第二个分支更快地到达结果(平均而言)。


它们是从(非常轻微的)不同的角度描述同一件事情的两个短语。并行编程是从硬件的角度描述情况——至少有两个处理器(可能在一个物理包中)并行处理一个问题。并发编程更多地是从软件的角度描述事情——两个或多个操作可能同时(并发)发生。

这里的问题是,人们试图用这两个短语来做出明确的区分,但实际上这两个短语并不存在。现实情况是,几十年来,他们试图划定的分界线一直是模糊的,而且随着时间的推移越来越模糊。

What they're trying to discuss is the fact that once upon a time, most computers had only a single CPU. When you executed multiple processes (or threads) on that single CPU, the CPU was only really executing one instruction from one of those threads at a time. The appearance of concurrency was an illusion--the CPU switching between executing instructions from different threads quickly enough that to human perception (to which anything less than 100 ms or so looks instantaneous) it looked like it was doing many things at once.

与此形成鲜明对比的是具有多个CPU或多核CPU的计算机,因此机器正在同时执行来自多个线程和/或进程的指令;执行其中一个的代码不能/不会对执行另一个的代码产生任何影响。

Now the problem: such a clean distinction has almost never existed. Computer designers are actually fairly intelligent, so they noticed a long time ago that (for example) when you needed to read some data from an I/O device such as a disk, it took a long time (in terms of CPU cycles) to finish. Instead of leaving the CPU idle while that happened, they figured out various ways of letting one process/thread make an I/O request, and let code from some other process/thread execute on the CPU while the I/O request completed.

因此,早在多核cpu成为标准之前,我们就有多个线程并行进行操作。

That's only the tip of the iceberg though. Decades ago, computers started providing another level of parallelism as well. Again, being fairly intelligent people, computer designers noticed that in a lot of cases, they had instructions that didn't affect each other, so it was possible to execute more than one instruction from the same stream at the same time. One early example that became pretty well known was the Control Data 6600. This was (by a fairly wide margin) the fastest computer on earth when it was introduced in 1964--and much of the same basic architecture remains in use today. It tracked the resources used by each instruction, and had a set of execution units that executed instructions as soon as the resources on which they depended became available, very similar to the design of most recent Intel/AMD processors.

But (as the commercials used to say) wait--that's not all. There's yet another design element to add still further confusion. It's been given quite a few different names (e.g., "Hyperthreading", "SMT", "CMP"), but they all refer to the same basic idea: a CPU that can execute multiple threads simultaneously, using a combination of some resources that are independent for each thread, and some resources that are shared between the threads. In a typical case this is combined with the instruction-level parallelism outlined above. To do that, we have two (or more) sets of architectural registers. Then we have a set of execution units that can execute instructions as soon as the necessary resources become available. These often combine well because the instructions from the separate streams virtually never depend on the same resources.

然后,当然,我们会讲到具有多核的现代系统。这里的情况很明显,对吧?我们有N个(目前大约在2到256之间)独立的内核,它们都可以同时执行指令,所以我们有了真正的并行性的清晰案例——在一个进程/线程中执行指令不会影响在另一个进程/线程中执行指令。

嗯,算是吧。即使在这里,我们也有一些独立的资源(寄存器、执行单元、至少一个级别的缓存)和一些共享资源(通常至少是最低级别的缓存,当然还有内存控制器和内存带宽)。

To summarize: the simple scenarios people like to contrast between shared resources and independent resources virtually never happen in real life. With all resources shared, we end up with something like MS-DOS, where we can only run one program at a time, and we have to stop running one before we can run the other at all. With completely independent resources, we have N computers running MS-DOS (without even a network to connect them) with no ability to share anything between them at all (because if we can even share a file, well, that's a shared resource, a violation of the basic premise of nothing being shared).

每个有趣的案例都涉及到独立资源和共享资源的某种组合。每一台相当现代的计算机(以及许多根本不现代的计算机)都至少有一些能力同时执行至少几个独立的操作,而任何比MS-DOS更复杂的东西都至少在某种程度上利用了这一点。

人们喜欢在“并发”和“并行”之间画出的漂亮、清晰的分界线根本不存在,而且几乎从来都不存在。人们喜欢归类为“并发”的东西通常仍然包含至少一种或更多不同类型的并行执行。他们喜欢归类为“并行”的内容通常涉及共享资源,(例如)一个进程在使用两个进程之间共享的资源时阻塞另一个进程的执行。

试图在“并行”和“并发”之间划清界限的人,其实是生活在一个从未真正存在过的计算机幻想中。


如果你的程序使用线程(并发编程),它不一定会这样执行(并行执行),因为这取决于机器是否可以处理几个线程。

这是一个直观的例子。非线程机器上的线程:

        --  --  --
     /              \
>---- --  --  --  -- ---->>

螺纹机上的螺纹:

     ------
    /      \
>-------------->>

虚线表示执行的代码。正如您所看到的,它们都分开并分别执行,但是线程机器可以同时执行几个单独的部分。


我的理解是:

1)并发-使用共享资源串联运行 2)使用不同的资源并行运行

所以你可以让两件事情同时发生,即使它们在点(2)聚集在一起,或者两件事情在整个执行的操作中占用相同的储备(1)。


Concurrent programming regards operations that appear to overlap and is primarily concerned with the complexity that arises due to non-deterministic control flow. The quantitative costs associated with concurrent programs are typically both throughput and latency. Concurrent programs are often IO bound but not always, e.g. concurrent garbage collectors are entirely on-CPU. The pedagogical example of a concurrent program is a web crawler. This program initiates requests for web pages and accepts the responses concurrently as the results of the downloads become available, accumulating a set of pages that have already been visited. Control flow is non-deterministic because the responses are not necessarily received in the same order each time the program is run. This characteristic can make it very hard to debug concurrent programs. Some applications are fundamentally concurrent, e.g. web servers must handle client connections concurrently. Erlang, F# asynchronous workflows and Scala's Akka library are perhaps the most promising approaches to highly concurrent programming.

Multicore programming is a special case of parallel programming. Parallel programming concerns operations that are overlapped for the specific goal of improving throughput. The difficulties of concurrent programming are evaded by making control flow deterministic. Typically, programs spawn sets of child tasks that run in parallel and the parent task only continues once every subtask has finished. This makes parallel programs much easier to debug than concurrent programs. The hard part of parallel programming is performance optimization with respect to issues such as granularity and communication. The latter is still an issue in the context of multicores because there is a considerable cost associated with transferring data from one cache to another. Dense matrix-matrix multiply is a pedagogical example of parallel programming and it can be solved efficiently by using Straasen's divide-and-conquer algorithm and attacking the sub-problems in parallel. Cilk is perhaps the most promising approach for high-performance parallel programming on multicores and it has been adopted in both Intel's Threaded Building Blocks and Microsoft's Task Parallel Library (in .NET 4).


我在一些博客上找到了这个内容。认为它是有用的和相关的。

并发性和并行性不是一回事。两个任务T1和T2是并发的,如果这两个任务的执行顺序不是预先确定的,

T1可以在T2之前执行和完成, T2可以在T1之前执行和完成, T1和T2可以在同一个时间实例中同时执行(并行性), T1和T2可以交替执行, ... 如果操作系统安排两个并发线程在一个单核非smt非cmp处理器上运行,您可能会得到并发性而不是并行性。并行在多核、多处理器或分布式系统上是可能的。

并发性通常被认为是程序的一种属性,是一个比并行性更普遍的概念。

来源:https://blogs.oracle.com/yuanlin/entry/concurrency_vs_parallelism_concurrent_programming


虽然没有完整 对并行和并发这两个术语的区别达成一致, 许多作者做了以下区分:

在并发计算中,一个程序可以在任意时刻执行多个任务。 在并行计算中,一个程序是多个任务紧密合作的程序 解决一个问题。

所以并行程序是并发的,但是像多任务操作系统这样的程序也是并发的,即使它运行在一台带有 只有一个核心,因为多个任务可以在任何时刻进行。

来源:Peter Pacheco的《并行编程介绍》


传统的任务调度可以是串行、并行或并发的。

Serial: tasks must be executed one after the other in a known tricked order or it will not work. Easy enough. Parallel: tasks must be executed at the same time or it will not work. Any failure of any of the tasks - functionally or in time - will result in total system failure. All tasks must have a common reliable sense of time. Try to avoid this or we will have tears by tea time. Concurrent: we do not care. We are not careless, though: we have analysed it and it doesn't matter; we can therefore execute any task using any available facility at any time. Happy days.

通常,在已知事件发生时,可用的调度会发生变化,我们称之为状态变化。

人们通常认为这是关于软件的,但实际上这是一种早于计算机的系统设计概念;软件系统的吸收速度有点慢,甚至很少有软件语言试图解决这个问题。如果你感兴趣,你可以试着查一下transputer language occam。

简而言之,系统设计解决以下问题:

动词——你在做什么(操作或算法) 名词——对(数据或接口)进行操作的对象 启动时,进度、状态改变 是串行、并行还是并发 地点——一旦你知道事情发生的时间,你就能说出事情可能发生的地点,而不是之前。 为什么,这是正确的方法吗?还有别的办法吗,更重要的是,更好的办法?如果你不做会怎么样?

祝你好运。


在编程中,并发是独立的组合 执行进程,而并行是同时执行 计算的(可能相关的)。 -安德鲁·格兰德

And

Concurrency is the composition of independently executing computations. Concurrency is a way to structure software, particularly as a way to write clean code that interacts well with the real world. It is not parallelism. Concurrency is not parallelism, although it enables parallelism. If you have only one processor, your program can still be concurrent but it cannot be parallel. On the other hand, a well-written concurrent program might run efficiently in parallel on a multiprocessor. That property could be important... - Rob Pike -

为了理解其中的区别,我强烈建议你去看看Rob Pike(Golang的创作者之一)的视频。并发不是并行


将原始问题解释为并行/并发计算,而不是编程。

在并发计算中,两个计算都是彼此独立前进的。第二个计算不需要等到第一个计算完成后才能继续进行。但是,它并没有说明这是如何实现的机制。在单核设置中,线程之间需要挂起和交替(也称为抢占式多线程)。

在并行计算中,两个计算同时进行——字面上是同时进行。这对于单CPU来说是不可能的,而是需要多核设置。

图片来自文章:“Node.js中的并行vs并发”


并行编程发生在代码同时被执行并且每次执行都是相互独立的时候。因此,通常不会有关于共享变量之类的关注,因为那不太可能发生。

However, concurrent programming consists on code being executed by different processes/threads that share variables and such, therefore on concurrent programming we must establish some sort of rule to decide which process/thread executes first, we want this so that we can be sure there will be consistency and that we can know with certainty what will happen. If there is no control and all threads compute at the same time and store things on the same variables, how would we know what to expect in the end? Maybe a thread is faster than the other, maybe one of the threads even stopped in the middle of its execution and another continued a different computation with a corrupted (not yet fully computed) variable, the possibilities are endless. It's in these situations that we usually use concurrent programming instead of parallel.


https://joearms.github.io/published/2013-04-05-concurrent-and-parallel-programming.html

并发=两个队列和一台咖啡机。

并行=两个队列和两个咖啡机。


从处理器的角度来看,它可以用这张图片来描述

从处理器的角度来看,它可以用这张图片来描述


并发编程在一般意义上是指我们定义的任务可以以任何顺序发生的环境。一个 任务可以发生在另一个任务之前或之后,并且部分或所有任务可以 同时进行的。 并行编程是特指在不同的处理器上同时执行并发任务。因此,所有 并行编程是并发的,但不是所有的并发编程 是平行的。

来源:PThreads Programming -一个更好的多处理POSIX标准,Buttlar, Farrell, Nichols


并发性和并行性源

在单个处理器上的多线程进程中,处理器可以在线程之间切换执行资源,从而实现并发执行。

在共享内存多处理器环境中的同一个多线程进程中,进程中的每个线程可以同时在单独的处理器上运行,从而导致并行执行。

当进程的线程数量与处理器数量相同或较少时,线程支持系统结合操作环境确保每个线程运行在不同的处理器上。

例如,在具有相同数量的线程和处理器的矩阵乘法中,每个线程(和每个处理器)计算结果的一行。


不同的人在许多不同的具体情况下讨论不同类型的并发性和并行性,因此需要一些抽象来涵盖它们的共同性质。

The basic abstraction is done in computer science, where both concurrency and parallelism are attributed to the properties of programs. Here, programs are formalized descriptions of computing. Such programs need not to be in any particular language or encoding, which is implementation-specific. The existence of API/ABI/ISA/OS is irrelevant to such level of abstraction. Surely one will need more detailed implementation-specific knowledge (like threading model) to do concrete programming works, the spirit behind the basic abstraction is not changed.

第二个重要的事实是,作为一般属性,并发性和并行性可以在许多不同的抽象中共存。

关于一般的区别,请参阅并发和并行的基本观点的相关答案。(还有一些链接包含一些其他来源。)

并发编程和并行编程是用一些系统实现这些一般属性的技术,这些系统公开了可编程性。系统通常是编程语言及其实现。

A programming language may expose the intended properties by built-in semantic rules. In most cases, such rules specify the evaluations of specific language structures (e.g. expressions) making the computation involved effectively concurrent or parallel. (More specifically, the computational effects implied by the evaluations can perfectly reflect these properties.) However, concurrent/parallel language semantics are essentially complex and they are not necessary to practical works (to implement efficient concurrent/parallel algorithms as the solutions of realistic problems). So, most traditional languages take a more conservative and simpler approach: assuming the semantics of evaluation totally sequential and serial, then providing optional primitives to allow some of the computations being concurrent and parallel. These primitives can be keywords or procedural constructs ("functions") supported by the language. They are implemented based on the interaction with hosted environments (OS, or "bare metal" hardware interface), usually opaque (not able to be derived using the language portably) to the language. Thus, in this particular kind of high-level abstractions seen by the programmers, nothing is concurrent/parallel besides these "magic" primitives and programs relying on these primitives; the programmers can then enjoy less error-prone experience of programming when concurrency/parallelism properties are not so interested.

Although primitives abstract the complex away in the most high-level abstractions, the implementations still have the extra complexity not exposed by the language feature. So, some mid-level abstractions are needed. One typical example is threading. Threading allows one or more thread of execution (or simply thread; sometimes it is also called a process, which is not necessarily the concept of a task scheduled in an OS) supported by the language implementation (the runtime). Threads are usually preemptively scheduled by the runtime, so a thread needs to know nothing about other threads. Thus, threads are natural to implement parallelism as long as they share nothing (the critical resources): just decompose computations in different threads, once the underlying implementation allows the overlapping of the computation resources during the execution, it works. Threads are also subject to concurrent accesses of shared resources: just access resources in any order meets the minimal constraints required by the algorithm, and the implementation will eventually determine when to access. In such cases, some synchronization operations may be necessary. Some languages treat threading and synchronization operations as parts of the high-level abstraction and expose them as primitives, while some other languages encourage only relatively more high-level primitives (like futures/promises) instead.

Under the level of language-specific threads, there come multitasking of the underlying hosting environment (typically, an OS). OS-level preemptive multitasking are used to implement (preemptive) multithreading. In some environments like Windows NT, the basic scheduling units (the tasks) are also "threads". To differentiate them with userspace implementation of threads mentioned above, they are called kernel threads, where "kernel" means the kernel of the OS (however, strictly speaking, this is not quite true for Windows NT; the "real" kernel is the NT executive). Kernel threads are not always 1:1 mapped to the userspace threads, although 1:1 mapping often reduces most overhead of mapping. Since kernel threads are heavyweight (involving system calls) to create/destroy/communicate, there are non 1:1 green threads in the userspace to overcome the overhead problems at the cost of the mapping overhead. The choice of mapping depending on the programming paradigm expected in the high-level abstraction. For example, when a huge number of userspace threads expected being concurrently executed (like Erlang), 1:1 mapping is never feasible.

The underlying of OS multitasking is ISA-level multitasking provided by the logical core of the processor. This is usually the most low-level public interface for programmers. Beneath this level, there may exist SMT. This is a form of more low-level multithreading implemented by the hardware, but arguably, still somewhat programmable - though it is usually only accessible by the processor manufacturer. Note the hardware design is apparently reflecting parallelism, but there is also concurrent scheduling mechanism to make the internal hardware resources being efficiently used.

在上面提到的每一层“线程”中,都涉及并发性和并行性。尽管编程接口变化很大,但它们都服从于一开始基本抽象所揭示的属性。


只是分享一个有助于突出区别的例子:

并行编程:假设您想实现归并排序算法。每次将问题划分为两个子问题时,可以有两个线程来解决它们。然而,为了进行合并步骤,您必须等待这两个线程完成,因为合并需要两个子解决方案。这种“强制等待”使其成为并行程序。

并发程序:假设你想压缩n个文本文件,并为每个文件生成一个压缩文件。您可以有2个(最多n个)线程,每个线程处理压缩文件的一个子集。当每个线程完成时,它就完成了,它不需要等待或做任何其他事情。因此,由于不同的任务以“任意顺序”交错的方式执行,所以程序是并发的,而不是并行的。

正如其他人提到的,每个并行程序都是并发的(事实上必须是),而不是相反。


我会试着用我自己的方式来解释它,它可能不是计算机术语,但它能给你一个大概的概念。

让我们以家务为例:洗碗,倒垃圾,修剪草坪等,我们有三个人(线程)A, B, C来做这些事情

并发: 三个人独立地开始不同的任务,例如,

A --> cleaning dishes
B --> taking out trash 
C --> mowing the lawn 

在这里,任务的顺序是不确定的,响应取决于工作量

并行: 在这里,如果我们想要提高吞吐量,我们可以分配多个人来完成一项任务,例如,清洁盘子,我们分配两个人,A擦洗盘子,B洗盘子,这可能会提高吞吐量。

洗碗:

A --> soaping the dishes
B --> washing the dishes

等等

希望这能给你一些启发!现在转到其他答案中解释的技术术语;)


并发性提供了一种构建解决方案的方法,以解决可能(但不一定)可并行的问题, 并发性是关于结构,并行性是关于执行。


并发:在单核机器上,多任务以cpu时间片共享的方式运行。 并行:在多核机器上,多个任务同时在每个核上运行。


并发编程是一个通用概念,即一个程序可以以未定义的完成顺序执行多个任务,并且这些任务可以同时执行,也可以不同时执行。

并行编程只是一种并发编程,其中这些任务运行在同时执行的线程上。

我真的不理解这里许多过于冗长的回答,这些回答似乎暗示并行编程和并行编程是不同的编程方法,它们并不重叠。

如果你在写一个并行程序,根据定义,你是在写一个并发程序的特殊情况。这些年来,术语似乎被不必要地混淆和复杂化了。

关于并发编程最好、最详细的报道之一是Joe Duffy所著的《Windows上的并发编程》一书。这本书定义了并发,然后继续解释各种操作系统资源,库等可用来编写“并行”程序,如。net中的任务并行库。

第5页:

并行性是使用并发性将操作分解为 粒度更细的组成部分,以便独立的部分可以运行 机器上的独立处理器"

同样,并行编程只是一种特殊类型的并发编程,其中多个线程/任务将同时运行。

PS 我一直不喜欢在编程中,并发和并行这两个词有如此多的含义。例:在编程之外的广阔世界里,“篮球比赛将并行进行”和“篮球比赛将并行进行”是完全相同的。

想象一下,在开发者大会上,他们在第一天宣传会议将“并行”运行,但第二天他们将“并发”运行,这是多么可笑的困惑。那会很搞笑的!