浅析Go中的MPG模式(二)――channel通讯

匿名 (未验证) 提交于 2019-12-03 00:09:02

首先拓展一下额外的内容:

runtime包介绍

(图片来自Go语言中文网)

一些较为重要的函数介绍

func NumCPU() int

使用NumCPU方法能够获得一个本地机器的逻辑CPU个数的int类型数值

func GOMAXPROCS(n int)  int

GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。本函数在调度程序优化后会去掉。
一般来说,在go1.8版本之后,系统会默认使用全部逻辑CPU进行并行操作。而在go1.8之前的版本,则需要自己设置(即使用GOMAXPROCS)。有的时候设计者需要保留一些CPU的功能占用,也会自主设定GOMAXPROCS比最大可用CPU数量少一些。
拓展完毕。接下来进入正题


那么不同的Goroutine之间如何通讯呢?有两种方式:
1、全局变量互斥锁
2、管道Channel

在Go语言中倡导:“不要以共享内存的方式来通信,相反,要通过通信来共享内存。”
首先看一下传统的并发形式:

多线程共享内存,这也是Java、C#或者C++等语言中的多线程开发的常规方法,其实golang语言也支持这种传统模式。

另外一种是Go语言特有的:CSP(Communicating Sequential Processes)并发模型。不同于传统的多线程通过共享内存来通信,CSP讲究的是“以通信的方式来共享内存”。

在讲同步通信之前先来看一下最简单基础的锁机制

锁机制

锁甚至可以直接理解为同步(Synchronization)。首先了解一下为什么会需要锁机制。在读取和写入这两种方法中,读取是不需要锁的,因为可以多个线程同时读取。而写就不可以,需要加入锁(像最简单的互斥锁Mutex),防止其他的线程进行干扰。
一般情况有两种锁,一种是互斥锁, 一种是读写锁,其中加了互斥锁的程序性能比读写锁的性能要低200倍
(图片来自Go语言中文网)


锁主要是为了防止线程之间对于资源的干扰,当一个线程对本身的操作加锁之后(Lock),那么别的线程就无法访问与其相关的内容,这样不会出现干扰,像写入异常等。最经典的就是Data race,资源争夺。
锁需要引入“sync”标准包
(图片来自Go语言中文网)

由图片对于sync标准包的介绍可以看到,锁一般适用于低水平程序线程,高水平同步应该使用channel通信更好。接下来进入Go的一大特色,channel管道。


管道Channel

先说一下为什么会需要channel。在前面说可以使用加锁来同步解决goroutine的通信,但是并不完美。因为主线程如果退出的话会导致未完成工作的goroutine协程提前退出,这样会出现各种各样的问题。
在我最开始的时候想到通过sleep来获取更多时间,使协程能够运行完毕。但是如果需要运算的内容特别庞大,那么时间就不好把控。sleep时间多了会加长等待切浪费资源,少了,主线程退出goroutine退出销毁程序异常。
而且还存在一个问题,比较不容易判断出在哪里需要增加锁(lock),哪里需要解除锁(unlock)。因此,go设计了一个新的通讯机制Channel。

下面基本介绍一下channel
1、 channel的本质就是一个数据结构-队列
2、channel遵循队列的读取顺序,先进先出(FIFO:first in first out)。
3、channel的线程是很安全的!它是由编译器在底层维护的。多个goroutine对同一个channel进行操作,都可以稳定安全的运行,不出现资源竞争问题。channel本身是不需要使用锁的,但是从源码可以看出,channel本身是有锁,使它本身的机制而来(底层代码实现),我们调用的时候并不需要再去添加或解除锁。
4、channel本身是有数据类型的。string类型的channel只能存放string类型的数据。一般我们都设置为interface{}类型,这样取的时候可以不用进行类型断言去取,免除了一些不必要的麻烦。


未完待续…

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!