Go 并发编程的思考

如果我必须选择 Go 的一个伟大特性,那么它必须是内置的并发模型 。Go 不仅支持并发性,而且使其更好,更易于使用 。Go 并发模型 (goroutine) 对并发编程的作用,就类似于 Docker 之于虚拟化的作用 。
什么是并发在计算机程序设计中,并发性指的是计算机同时处理多个任务的能力 。例如,如果你在浏览器中上网,可能会有很多事情同时在发生 。比如,你正在下载一些文件,同时滚动页面来收听音乐 。因此浏览器需要同时处理这两件事情 。如果浏览器无法处理这些问题,则需要等到所有下载任务完成,然后才能够重新浏览网站,这对于用户来说是一件很痛苦的事情 。
一台通用的 PC 机可能只有一个 CPU 核心来完成所有的任务,一个 CPU 核心可以一次处理一件事 。当我们讨论并发性的时候,指的的是我们将 CPU 的时间片分配给需要处理的事情 。因此,我们感觉到有很多事情在同时发生 。
让我们来看一个CPU管理web浏览器如何让处理我们示例图表中的内容 。
从上图中,你可以看到一个单核处理器根据每个任务的优先级来划分工作负载 。例如,当页面滚动的时候,听音乐的优先级可能很低,因此有时你的音乐会因为网速低而停止,但是你仍然可以滚动页面 。单个处理器通过某种切换时间片调度任务执行的策略,让用户感受到多个任务在同时执行 。

Go 并发编程的思考

文章插图
 
什么是并行接下来问题来了,如果我们CPU有多个内核呢?实际上现代的CPU上都是多核架构,如果一个CPU有多个处理核心,我们就把它叫做“多核处理器” 。你可能在购买电脑或者智能手机的时候听说过这个词,例如我目前工作的笔记本就是2核心的,相对于目前比较先进的个人电脑 CPU 来说,是很 LOW的了 。而且
商用服务器一般达到了 64 核心的处理能力,多个处理其能够同时处理多个任务 。在前面 web 浏览器的示例中,我们的单核处理器必须将 CPU 时间分配给不同的任务对象 。使用多核处理器,我们可以在不同的核中同时运行不同的任务,可以看到下图 。
Go 并发编程的思考

文章插图
 
同时运行多个任务的概念我们称之为并行 。当我们的 CPU 有多个内核时,我们可以使用不同的 CPU 内核来同时执行多个任务,因此我们可以很快的去完成一项包括很多任务的工作 。
Go 并发编程的思考

文章插图
 
并发 vs 并行
Go 建议只在一个内核上使用 goruntines,但是我们可以修改 Go 程序,以遍在不同的处理器内核上运行 goruntines 。
并发和并行之前有几个区别 。并发是交替的处理多个事情,并行则是同时处理多个事情 。那是不是并行一定会被并发更有益呢?也不一定,我们会在后续的播客里面讨论到这一点
现在,可能会有很多问题在你的脑海里飞舞,你可能已经建立的并行和并发的想法,但你可能想知道如何使用 Go 的并发体系去实现它,在这之前我们先来了解一下计算机进程 。
什么是计算机进程?当你用 C、JAVA 或 Go 等语言编写一个计算机程序时,它只是一个文本文件 。但是由于计算机是只理解 0和1组成的二进制指令,所以需要将该代码翻译成机器语言 。这就是编译器的用武之地 。在像 Python 和 js这样的脚本语言中,解释器做同样的事情 。当编译后的程序被发送到操作系统进行处理时,操作系统会分配不同的东西,比如内存地址空间(进程的堆和堆栈位于其中)、程序计数器、进程id (PID) 和其他关键的东西 。进程至少又一个称之为主线程的线程,而主线程可以创建多个其他线程 。当主线程执行完成时,进程退出 。
所以我们可以理解进程就是一个容器,它编译了代码,内存、不同的操作系统资源和其他可以提供给线程的东西 。简而言之,进程就是内存中的一个程序 。
什么是计算机线程?线程是一段代码的实际执行者 。线程可以访问进程提供的内存、操作系统资源和其他东西 。执行程序代码是,内存区域内的线程存储变量(数据)被称为堆栈,其中暂存的变量占用堆栈空间,堆栈是在运行时创建的,通常具有固定大小,最好是1-2MB,线程堆栈只能由改线程使用,并且不会与其他线程共享 。堆是进程的属性,任何线程都可以使用它,堆是一个共享的内存空间,一个线程中的数据也可以被其他线程访问 。
现在,我们对进程和线程有了一个大致的了解 。但是它们有什么用呢?


推荐阅读