初识go的tomb包

 在分析github.com/hpcloud/tail 这个包的源码的时候,发现这个包里用于了一个另外一个包,自己也没有用过,但是这个包在tail这个包里又起来非常大的作用

当时并没有完全弄明白这个包的用法和作用,所以又花时间找了这个包的使用和相关文档,其中看了https://blog.labix.org/2011/10/09/death-of-goroutines-under-control 这篇文章整理的挺好的,自己对这个文章进行了简单的翻译,下面这个文章中的使用是gopkg.in/tomb.v2

Death of goroutines under control

很多人被go语言吸引的原因是其非常好的并发性,以及channel, 轻量级线程(goroutine)等这些特性

并且你也会经常在社区或者其他文章里看到这样一句话:

Do not communicate by sharing memory;

instead, share memory by communicating.

这个模型非常合理,以这种方式处理问题,在设计算法时会产生显著差异,但这个并不是什么新闻

What I address in this post is an open aspect we have today in Go related to this design: the termination of background activity.(不知道怎么翻译了)

作为一个例子,我们构建一个简单的goroutine,通过一个channel 发送

 

复制代码
type LineReader struct {         Ch chan string         r  *bufio.Reader }  func NewLineReader(r io.Reader) *LineReader {         lr := &LineReader{                 Ch: make(chan string),                 r:  bufio.NewReader(r),         }         go lr.loop()         return lr }
复制代码

The type has a channel where the client can consume lines from, and an internal buffer
used to produce the lines efficiently. Then, we have a function that creates an initialized
reader, fires the reading loop, and returns. Nothing surprising there.

接着看看loop方法

复制代码
func (lr *LineReader) loop() {         for {                 line, err := lr.r.ReadSlice('\n')                 if err != nil {                         close(lr.Ch)                         return                 }                 lr.Ch <- string(line)         } }
复制代码

在这个loop中,我们将从从缓冲器中获取每行的内容,如果出现错误时关闭通道并停止,否则则将读取的一行内容放到channel中

也许channel接受的那一方忙于其他处理,而导致其会阻塞。 这个简单的例子对于很多go开发者应该非常熟悉了

 

但这里有两个与终止逻辑相关的细节:首先错误信息被丢弃,然后无法通过一种更加干净的方式从外部终端程序

当然,错误很容易记录下来,但是如果我们想要将它存储数据库中,或者通过网络发送它,或者甚至考虑到它的性质,在很过情况下

干净的停止也是非常有价值的

这并非是一个非常难以做到的事情,但是今天没有简单一致的方法来处理,或者也许没有,而go中的Tom包就是试图解决这个问题的

 

这个模型很简单:tomb跟踪一个或者多个goroutines是活着的,还是已经死了,以及死亡的原因

为了理解这个模型,我们把上面的LineReader例子进行改写:

复制代码
type LineReader struct {         Ch chan string         r  *bufio.Reader         t  tomb.Tomb }  func NewLineReader(r io.Reader) *LineReader {         lr := &LineReader{                 Ch: make(chan string),                 r:  bufio.NewReader(r),         }         lr.t.Go(lr.loop)         return lr }
复制代码

这里有一些有趣的点:

首先,现在出现的错误结果与任何可能失败的go函数或者方法一样。

hen, the previously loose error is now returned, 标记这个goroutine终止的原因,

最后这个通道的发送被调增,以便于不管goroutine因为上面原因死亡都不会阻塞

A Tomb has both Dying and Dead channels returned by the respective methods, which are closed when the Tomb state changes accordingly. These channels enable explicit blocking until the state changes, and also to selectively unblock select statements in those cases, as done above.

 

通过上面的说的,我们可以简单的引入Stop方法从外部同步请求清楚goroutine

复制代码
func (lr *LineReader) Stop() error {         lr.t.Kill(nil)         return lr.t.Wait() }
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信