Go 语言入门与进阶:并发模型

这是我参与更文挑战的第 24 天,活动详情查看: 更文挑战

前文回顾

如果你还没有 Go 语言基础,建议阅读我的 从零学 Go

本系列文章,我将会进一步加深对 Go 语言的讲解,更一步介绍 Go 中的包管理、反射和并发等高级特性。
前面一篇文章主要介绍 Go 语言的反射 reflect.Value ,如何判断一个变量的 Value 是否可设,调用函数等功能。本文将会介绍并发模型相关的概念。

集成电路中晶体管的数量按照摩尔定律的推测趋势已经持续增长了超过半个世纪,CPU 的性能在持续提升。但是就目前看来,“免费午餐的时代已然结束”,芯片中晶体管数量密度的增加速度已然放缓。为了让代码跑得更多,单纯依靠硬件的提升已经不能满足我们的需求,我们需要多核运行的支持,使程序能够并发或者并行执行。

并发与并行

并发和并行都是为了充分利用 CPU 多核计算资源所提出来的概念,相信我们或多或少都对二者的概念有所了解:

  • 并发指的是在同一时间段内,多条指令在 CPU 上同时执行;
  • 并行指的是在同一时刻内,多条指令在 CPU 上同时执行。

并发程序并不要求 CPU 具备多核计算的能力。在同一时间段内,多个线程会被分配一定的执行时间片,在 CPU 上被快速轮换执行。线程执行的时间片时间耗尽或者任务完成了,会被 CPU 调度换下,执行其他的线程任务。通过这样的方式,可以在宏观上模拟出多个线程同时执行的效果。

而并行程序要求 CPU 提供多核并行计算的能力。在同一时刻内,就有多个线程在 CPU 上的多个核上同时执行指令。无论从宏观还是微观上观察,都会多个线程在同时执行。

并发程序的执行通常是不确定的,这种不确定来源于资源之间的相关依赖和竞态条件,这可能导致执行线程之间的相互等待,使得并发程序即时在多核环境上也无法做到真正并行执行而降级为串行执行。并行程序的每个执行模块在逻辑上是独立的,即线程执行时可以独立完成任务,从而做到同一时刻多个指令能够同时执行。

CSP 并发模型

Go 语言中实现了两种并发模式,一种是我们熟悉的线程与锁并发模型,它主要依赖于共享内存实现。线程与锁模型类似于对底层硬件运行过程的形式化,程序的正确运行很大程度依赖于开发人员的能力和技巧,程序在出错时不易排查。另一种是 Go 中倡导使用的 CSP (communicating sequential processes)通信顺序进程模型。

CSP 并发模型最初由 Tony Hoare 于 1977 年的论文中被提出,它倡导使用通信的手段来共享内存。CSP 模型中存在两个关键的概念:

  • 并发实体,通常可以理解为执行线程,它们相互独立,且并发执行;
  • 通道,并发实体之间使用通道发送信息。

与共享内存的线程与锁并发模型不同,CSP 中的并发实体是独立的,它们之间没有共享的内存空间。并发实体之间的数据交换通过通道实现,无论在通道中放数据还是从通道中取数据,都会导致并发实体的阻塞,直到通道中的数据被取出或者通道中被放入新的数据,并发实体通过这种方式实现同步。

CSP 类似于我们常用的同步队列,它关注的是消息传输的方式,即通道,消息的具体发送实体和具体接收实体并不关注。发送和接收信息的并发实体可能不知道对方具体是谁,它们之间是互相解耦的。通道与并发实体也不是紧耦合的,通道可以独立地进行创建和放取,并在不同的并发实体中传递使用。

CSP 通道的特性给并发编程提供了极大的灵活性,通道作为独立的对象,可以被任意创建、读取、放入数据,并在不同的并发实体中被使用。但是它也极易导致死锁,如果一个并发实体在读取一个永远没有数据放入的通道或者把数据放入一个永远不会被读取的通道中,那么它将被永远阻塞。

小结

本文主要介绍了并发与并行的相关概念与区别,以及介绍了 CSP 并发模型,Go 中倡导使用的 CSP (communicating sequential processes)通信顺序进程模型。下一篇文章将会开始介绍 Go 语言常见的线程模型。

阅读最新文章,关注公众号:aoho求索

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享