「Go语言进阶」并发编程详解

在很多情况下 , 并发的效果比并行好 , 因为操作系统和硬件的总资源一般很少 , 但能支持系统同时做很多事情 。这种“使用较少的资源做更多的事情”的哲学 , 也是指导 Go语言设计的哲学 。并发 VS 并行在讲解并发概念时 , 总会涉及另外一个概念并行 。下面让我们来聊聊并发和并行之间的区别 。

  • 并发(concurrency):把任务在不同的时间点交给处理器进行处理 。在同一时间点 , 任务并不会同时运行 。
  • 并行(parallelism):把每一个任务分配给每一个处理器独立完成 。在同一时间点 , 任务一定是同时运行 。
并发不是并行 。并行是让不同的代码片段同时在不同的物理处理器上执行 。并行的关键是同时做很多事情 , 而并发是指同时管理很多事情 , 这些事情可能只做了一半就被暂停去做别的事情了 。
在很多情况下 , 并发的效果比并行好 , 因为操作系统和硬件的总资源一般很少 , 但能支持系统同时做很多事情 。这种“使用较少的资源做更多的事情”的哲学 , 也是指导 Go语言设计的哲学 。
如果希望让 goroutine 并行 , 必须使用多于一个逻辑处理器 。当有多个逻辑处理器(CPU)时 , 调度器会将 goroutine 平等分配到每个逻辑处理器上 。这会让 goroutine 在不同的线程上运行 。不过要想真的实现并行的效果 , 用户需要让自己的程序运行在有多个物理处理器的机器上 。否则 , 哪怕 Go语言运行时使用多个线程 , goroutine 依然会在同一个物理处理器上并发运行 , 达不到并行的效果 。
下图展示了在一个逻辑处理器上并发运行 goroutine 和在两个逻辑处理器上并行运行两个并发的 goroutine 之间的区别 。 调度器包含一些聪明的算法 , 这些算法会随着Go语言的发布被更新和改进 , 所以不推荐盲目修改语言运行时对逻辑处理器的默认设置 。如果真的认为修改逻辑处理器的数量可以改进性能 , 也可以对语言运行时的参数进行细微调整 。
【「Go语言进阶」并发编程详解】 
「Go语言进阶」并发编程详解

文章插图
 
并发与并行的区别
Go 可以充分发挥多核优势 , 高效运行 。 Go语言在 GOMAXPROCS 数量与任务数量相等时 , 可以做到并行执行 , 但一般情况下都是并发执行 。
目录
  • 1.1 Goroutine
  • 1.2 CSP
  • 1.3 Channel
  • 1.4 Lock
  • 1.5 WaitGroup
1.1 Goroutine由谁创建?
  • 线程是操作系统分配给应用程序的独立执行单元 , 它们可以在多核处理器中并行执行 。线程的调度是由操作系统内核负责的 , 并且线程之间有独立的地址空间 。
  • 协程是由程序员编写的 , 它是一种轻量级的线程 , 并由Go语言运行时管理 。协程之间没有独立的地址空间 , 而是共享一个地址空间 。协程的调度是由Go语言运行时负责的 , 并且可以在单个线程中并行执行 。
线程的创建和销毁的开销比较大 , 而协程的创建和销毁开销很小 , 因此在需要高并发的场景中 , 使用协程更加高效 。
大小比较?线程栈是由操作系统分配的 , 它通常有一个固定的大小 , 并且在线程创建时分配 。它存储着线程的状态信息和调用栈 。线程栈的大小取决于操作系统的限制 , 一般在几百KB到几MB之间 。
而协程的栈是由Go语言运行时管理的 , 它通常有一个较小的默认大小 , 并在协程创建时分配 。它也存储着协程的状态信息和调用栈 。协程栈的大小可以通过Golang的runtime包中的函数来调整 , 一般在几KB到几MB之间 。
由于协程的栈比线程栈小 , 所以协程能够创建的数量比线程多得多 。但是由于协程栈比线程栈小 , 所以在调用深度较深的程序中 , 协程可能会爆栈 。
1.2 CSPCSP:Communicating Sequential Processes
Go语言提倡:通过通信共享内存 , 而不是通过共享内存而实现通信 。


推荐阅读