Golang 的语法和运行时直接内置了对并发的支持。
Golang 里的并发指的是能让某个函数独立于其他函数运行的能力。当一个函数创建为 goroutine 时,Golang 会将其视为一个独立的工作单元。这个单元会被调度到可用的逻辑处理器上执行。Golang 运行时的调度器是一个复杂的软件,能管理被创建的所有 goroutine 并为其分配执行时间。这个调度器在操作系统之上,将操作系统的线程与语言运行时的逻辑处理器绑定,并在逻辑处理器上运行 goroutine。调度器在任何给定的时间,都会全面控制哪个 goroutine 要在哪个逻辑处理器上运行。

Golang 的并发同步模型来自一个叫作通信顺序进程(Communicating Sequential Processes,CSP)的范型(paradigm)。CSP 是一种消息传递模型,通过在 goroutine 之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问。用于在 goroutine 之间同步和传递数据的关键数据类型叫作通道(channel)。

进程与线程

进程
当运行一个应用程序的时候,操作系统会为这个应用程序启动一个进程。可以将这个进程看作一个包含了应用程序在运行中需要用到和维护的各种资源的容器。这些资源包括但不限于内存地址空间、文件和设备的句柄以及线程。

线程
一个线程是一个执行空间,这个空间会被操作系统调度来运行函数中所写的代码。每个进程至少包含一个线程,每个进程的初始线程被称作主线程。因为执行这个线程的空间是应用程序的本身的空间,所以当主线程终止时,应用程序也会终止。操作系统将线程调度到某个处理器上运行,这个处理器并不一定是进程所在的处理器。下图展示了一个运行中的应用程序的进程和线程视图(下图来自互联网):

本质上,无论 windows 还是 linux,操作系统调度的单位都是线程(调度线程在 CPU 上执行)。

逻辑处理器与本地运行队列

逻辑处理器
Golang 的运行时会在逻辑处理器上调度 goroutine 来运行。每个逻辑处理器都与一个操作系统线程绑定。在 Golang 1.5 及以后的版本中,运行时默认会为每个可用的物理处理器分配一个逻辑处理器。

本地运行队列
每个逻辑处理器有一个本地运行队列。如果创建一个 goroutine 并准备运行,这个 goroutine 首先会被放到调度器的全局运行队列中。之后,调度器会将全局运行队列中的 goroutine 分配给一个逻辑处理器,并放到这个逻辑处理器的本地运行队列中。本地运行队列中的 goroutine 会一直等待直到被分配的逻辑处理器执行。

下图展示了操作系统线程、逻辑处理器和本地运行队列之间的关系(下图来自互联网):

有时,正在运行的 goroutine 需要执行一个阻塞的系统调用,如打开一个文件。当这类调用发生时,线程和 goroutine 会从逻辑处理器上分离,该线程会继续阻塞,等待系统调用的返回。与此同时,这个逻辑处理器就失去了用来运行的线程。所以,调度器会创建一个新线程,并将其绑定到该逻辑处理器上。之后,调度器会从本地运行队列里选择另一个 goroutine 来运行。一旦被阻塞的系统调用执行完成并返回,对应的 goroutine 会放回到本地运行队列,而之前的线程会保存好,以便之后可以继续使用。看下面的图示(下图来自互联网):

如果一个 goroutine 需要做一个网络 I/O 调用,流程上会有些不一样。在这种情况下,goroutine 会和逻辑处理器分离,并移到集成了网络轮询器的运行时。一旦该轮询器指示某个网络读或者写操作已经就绪,对应的goroutine 就会重新分配到逻辑处理器上来完成操作。

注意:Golang 运行时默认限制每个程序最多创建 10000 个线程。这个限制值可以通过调用 runtime/debug 包的 SetMaxThreads 方法来更改。如果程序试图使用更多的线程,就会崩溃。

并发与并行

并发(concurrency)与并行(parallelism)不同。并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了(Golang 的并发通过切换多个线程达到减少物理处理器空闲等待的目的)。在很多情况下,并发的效果比并行好,因为操作系统和硬件的总资源一般很少,但能支持系统同时做很多事情。这种 "使用较少的资源做更多的事情" 的哲学,也是指导 Golang 设计的哲学。

如果希望让 goroutine 并行,必须使用多于一个逻辑处理器。当有多个逻辑处理器时,调度器会将 goroutine 平等分配到每个逻辑处理器上。这会让 goroutine 在不同的线程上运行。不过要想真的实现并行的效果,用户需要让自己的程序运行在有多个物理处理器的机器上。否则,哪怕 Golang 运行时使用多个线程,goroutine 依然会在同一个物理处理器上并发运行,达不到并行的效果。下图展示了在一个逻辑处理器上并发运行 goroutine 和在两个逻辑处理器上并行运行两个并发的 goroutine 之间的区别(下图来自互联网):

参考:
《Go语言实战》

Golang 入门 : 理解并发与并行的更多相关文章

  1. Golang 入门 : 竞争条件

    笔者在前文<Golang 入门 : 理解并发与并行>和<Golang 入门 : goroutine(协程)>中介绍了 Golang 对并发的原生支持以及 goroutine 的 ...

  2. Golang 入门 : goroutine(协程)

    在操作系统中,执行体是个抽象的概念.与之对应的实体有进程.线程以及协程(coroutine).协程也叫轻量级的线程,与传统的进程和线程相比,协程的最大特点是 "轻"!可以轻松创建上 ...

  3. 图解并发与并行-分别从CPU和线程的角度理解

    本文作为图解java并发编程的第三篇,前2篇访问地址如下所示: 图解进程线程.互斥锁与信号量-看完还不懂你来打我 8成以上的java线程状态图都画错了--图解java并发第二篇 一.CPU角度的并发与 ...

  4. Golang 入门系列(十五)如何理解go的并发?

    前面已经讲过很多Golang系列知识,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html, 接下来要说的 ...

  5. Golang入门(4):并发

    摘要 并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要.Web服务器会一次处理成千上万的请求,这也是并发的必要性之一.Golang的并发控制比起Java来说,简单了不少.在Go ...

  6. JS异步解决方案之概念理解-----------阻塞和非阻塞,同步和异步,并发和并行,单线程和多线程

    首先记住一句话,JS是单线程的. 单线程意味着什么?单线程意味着 它不能依靠自己实现异步. JS实现的异步,往往都是靠 浏览器.Node 的机制(事件驱动.回调)实现的. 下面让我这个单身狗 以谈恋爱 ...

  7. Golang 入门系列(十七)几个常见的并发模型——生产者消费者模型

    前面已经讲过很多Golang系列知识,包括并发,锁等内容,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.ht ...

  8. go并发和并行

    Go语言的并发和并行 不知道你有没有注意到一个现象,还是这段代码,如果我跑在两个goroutines里面的话: var quit chan int = make(chan int) func loop ...

  9. Go语言并发与并行学习笔记(二)

    转:http://blog.csdn.net/kjfcpua/article/details/18265461 Go语言的并发和并行 不知道你有没有注意到一个现象,还是这段代码,如果我跑在两个goro ...

随机推荐

  1. 洛谷 P3879 [TJOI2010]阅读理解

    P3879 [TJOI2010]阅读理解 题目描述 英语老师留了N篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过. 输入输出 ...

  2. HashCode和equal方法

    equals()反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值. 而hashCode()是对象或变量通过哈希算法计算出的哈希值. 之所以有hashCode方 ...

  3. EF关联

    public CustomerMap() { this.ToTable("Customer"); this.HasKey(c => c.Id); this.Property( ...

  4. Client使用c#和odp.net连接server oracle

    http://blog.csdn.net/educast/article/details/6605655 Oracle.DataAccess.dll有2.X版本和4.X版本,VS2008开发用2.X ...

  5. vue自定义轮播图组件 swiper

    1.banner 组件 components/Banner.vue <!-- 轮播图 组件 --> <template> <div class="swiper- ...

  6. JavaSE----API之集合(Collection、List及其子类、Set及其子类、JDK1.5新特性)

    5.集合类 集合类的由来: 对象用于封装特有数据,对象多了须要存储:假设对象的个数不确定.就使用集合容器进行存储. 集合容器由于内部的数据结构不同,有多种详细容器.不断的向上抽取,就形成了集合框架. ...

  7. PHP使用debug_backtrace方法跟踪代码调用

    在开发过程中,例如要修改别人开发的代码或调试出问题的代码,需要对代码流程一步步去跟踪,找到出问题的地方进行修改.如果有一个方法可以获取到某段代码是被哪个方法调用,并能一直回溯到最开始调用的地方(包括调 ...

  8. 硬件开发之bt输出---BT656/BT601/BT1120协议以及DM365/DM355/DM6467上使用的YUV颜色空间说明

    http://blog.csdn.net/zhouzhuan2008/article/details/17168133

  9. 图片懒加载和UA池,UA代理池

    1,动态数据加载的处理 图片懒加载概念: 图片懒加载是一种页面优化技术.图片作为一种网络资源,在被请求时也与静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面首屏加载时间 ...

  10. Codeforces Round #412 (rated, Div. 2, base on VK Cup 2017 Round 3) E. Prairie Partition 二分+贪心

    E. Prairie Partition It can be shown that any positive integer x can be uniquely represented as x =  ...