via:
https://medium.com/technofunnel/understanding-goroutine-go-channels-in-detail-9c5a28f08e0d作者:Mayank Gupta
四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!
这篇文章源自 Medium,文章点赞 600+。
这篇文章的内容很简洁,知识点容易理解,很佩服作者能把知识点讲的通俗易懂,可能这就是文章获得高点赞的原因吧。
原文如下:
Go 语言使用 goroutine 可以轻松地实现并发。大家应该都知道,goroutine 是通过 channel 实现相互通信的。channel 确保 goroutine 和主线程时间可以相互通信。
在这篇文章中,将会与大家讨论如何创建 channel 和实现数据共享。
简单介绍下 channel
channel 给编程带来了很大的灵活性,并解决了涉及并发的各种问题。总结有如下几点:
- 是一种通信机制;
- channel 可以作为参数发送给不同的 goroutine;
- 既可充当发布者又可充当订阅者;
内存隔离
以前,程序通过全局变量的方式实现线程之间共享数据,我们不得不跟踪不同线程对数据的操作。
全局共享内存会导致与不同线程之间同步数据有关的诸多问题。
Go 语言通过 channel 实现了安全的数据通信,只有一个子线程可以对数据进行操作。channel 里的有效数据只能被单个 goroutine 访问。数据的接收方和发送方是一对一的关系。
第一步:创建 channel
我们来看下上面代码值得关注的几个细节:
- 使用 make 关键字创建新的对象;
- 需要指定使用 chan 创建的对象类型;
- 使用 string 指定 channel 返回的数据类型;
所以第 5 行代码,我们创建了 channel 类型的对象,goroutine 之间可以用来传递 string 类型的数据。
等待获取 channel 的数据
上面第 6 行代码,main 函数会一直等待,直到从 channel 接收到数据为止。上面的代码,没任何其他 goroutine 向 channel 发送数据,程序会在等待接收数据过程发生死锁。
我们看下输出:
从运行结果可以看出,程序发生了死锁,因为 main 函数在一直等待 channel 返回数据。
第二步:往 channel 添加数据
上面的代码中,我们创建了 channel,并等待 channel 返回可用数据。由于未返回数据,程序发生死锁。
下一步,我们将在 main() 函数里为 channel 提供数据,一起来看下下面的代码:
上面的代码中,往 channel 添加了一些简单的数据,现在你是不是认为可以从 channel 接收到数据呢?
一起来看下输出:
从上面的输出可以看出,程序再次发生死锁。你能想出为什么吗?
上面的代码中,我们往 channel 添加数据。一旦往 channel 发送数据,主协程便发生阻塞直到有其他协程将数据从 channel 取出。由于没有其他协程将数据取出,主协程便一直阻塞,进而导致死锁。
第三步:解决死锁
需要重申一点,默认情况下,channel 是不带缓存的,这意味着数据需要被接收者立即取出,发送者协程才能继续正常工作。
如果没有协程接收数据,发送者协程将会一直阻塞。
通过带缓存区的 channel,我们可以在 channel 里存储数据,只要缓存没满,即使数据没被取出,发送者协程也可以正常工作。
我们可以使用带缓存的 channel 解决上述问题。
创建带缓存的 channel
为了保证即使在数据没被其他协程接收的情况下,发送者协程仍能正常工作,可以使用带缓存的 channel。
我们一起来看下例子:
上面的代码,我们创建了缓存容量为 3 的 channel,意味着该 channel 可以存储 3 个 string 值。往 channel 添加的数据可以不用立即处理,直到缓存满之前,我们可以一直往 channel 添加数据。
一旦缓存满了,我们就必须处理数据否则也会发生死锁。
因此,使用带缓存的 channel,允许协程将数据保存在 channel 中,以便在后续程序执行过程中取出,从而消除死锁。
让我们看下面的输出:
从输出结果可以看出,死锁已经消除了。
如果我的文章对你有所帮助,点赞、转发都是一种支持!