在分析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()
}

在这种情况下,Kill方法会将正在运行的goroutine从外部将其置于一个死亡状态,并且Wait将阻塞直到goroutine通过

自己终止返回。 即使由于内部错误,groutine已经死亡或者处于死亡状态,此过程也会正常运行,因为只有第一次用一个实际的错误调用Kill被记录为goroutine死亡原因。

The nil value provided to t.Kill is used as a reason when terminating cleanly without an actual error, and it causes Wait to return nil once the goroutine terminates, flagging a clean stop per common Go idioms.

关于gopkg.in/tomb.v2的官网说明的一段话:

The tomb package handles clean goroutine tracking and termination.

The zero value of a Tomb is ready to handle the creation of a tracked goroutine via its Go method, and then any tracked goroutine may call the Go method again to create additional tracked goroutines at any point.

If any of the tracked goroutines returns a non-nil error, or the Kill or Killf method is called by any goroutine in the system (tracked or not), the tomb Err is set, Alive is set to false, and the Dying channel is closed to flag that all tracked goroutines are supposed to willingly terminate as soon as possible.

Once all tracked goroutines terminate, the Dead channel is closed, and Wait unblocks and returns the first non-nil error presented to the tomb via a result or an explicit Kill or Killf method call, or nil if there were no errors.

It is okay to create further goroutines via the Go method while the tomb is in a dying state. The final dead state is only reached once all tracked goroutines terminate, at which point calling the Go method again will cause a runtime panic.

Tracked functions and methods that are still running while the tomb is in dying state may choose to return ErrDying as their error value. This preserves the well established non-nil error convention, but is understood by the tomb as a clean termination. The Err and Wait methods will still return nil if all observed errors were either nil or ErrDying.

关于gopkg.in/tomb.v1使用例子

在golang官网上看到了这样一个例子,觉得用的挺好的就放这里

package main

import (
"gopkg.in/tomb.v1"
"log"
"sync"
"time"
) type foo struct {
tomb tomb.Tomb
wg sync.WaitGroup
} func (f *foo) task(id int) {
for i := ; i < ; i++ {
select {
case <-time.After(1e9):
log.Printf("task %d tick\n", id)
case <-f.tomb.Dying():
log.Printf("task %d stopping\n", id)
f.wg.Done()
return
}
}
} func (f *foo) Run() {
f.wg.Add()
for i := ; i < ; i++ {
go f.task(i)
}
go func() {
f.wg.Wait()
f.tomb.Done()
}()
} func (f *foo) Stop() error {
f.tomb.Kill(nil)
return f.tomb.Wait()
} func main() {
var f foo
f.Run()
time.Sleep(3.5e9)
log.Printf("calling stop\n")
f.Stop()
log.Printf("all done\n")
}

在关于tomb这个包的说明上,说的也非常清楚,tomb包用于追踪一个goroutine的声明周期,如:as alive,dying or dead and the reason for its death

关于v1 版本官网的说明

The tomb package offers a conventional API for clean goroutine termination.

A Tomb tracks the lifecycle of a goroutine as alive, dying or dead, and the reason for its death.

The zero value of a Tomb assumes that a goroutine is about to be created or already alive. Once Kill or Killf is called with an argument that informs the reason for death, the goroutine is in a dying state and is expected to terminate soon. Right before the goroutine function or method returns, Done must be called to inform that the goroutine is indeed dead and about to stop running.

A Tomb exposes Dying and Dead channels. These channels are closed when the Tomb state changes in the respective way. They enable explicit blocking until the state changes, and also to selectively unblock select statements accordingly.

When the tomb state changes to dying and there's still logic going on within the goroutine, nested functions and methods may choose to return ErrDying as their error value, as this error won't alter the tomb state if provided to the Kill method. This is a convenient way to follow standard Go practices in the context of a dying tomb..

小结

可以从上面的文章以及使用例子上看出,tomb包是一个非常实用的一个包,后面会继续整理一下关于tomb v1版本的源码,看看人家是如何实现的,学习学习

初识go的tomb包的更多相关文章

  1. 初识Http协议抓包工具—Fiddler

    1.Fiddler简介 Fiddler是用一款使用C#编写的http协议调试代理工具.它支持众多的http调试任务,能够记录并检查所有你的电脑和互联网之间的http通讯,可以设置断点,查看所有的“进出 ...

  2. Fiddler系列教程1:初识Http协议抓包工具

    1. Fiddler简介 Fiddler是用一款使用C#编写的http协议调试代理工具.它支持众多的http调试任务,能够记录并检查所有你的电脑和互联网之间的http通讯,可以设置断点,查看所有的“进 ...

  3. Linux usb 2. 协议分析

    文章目录 0. 背景 1. USB 协议传输格式 1.1 Packet 1.1.1 Token Packet 1.1.2 Data Packet 1.1.3 Handshake Packet 1.1. ...

  4. 黏包-黏包的成因、解决方式及struct模块初识、文件的上传和下载

    黏包: 同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包. 只有TCP协议中才会产生黏包,UDP协议中不会有黏包(udp协议中数 ...

  5. 初识node.js(通过npm下载项目依赖的包的过程)

    一.初识node.js 简单的说Node.js 就是运行在服务器端的JavaScript. Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台. Node.js是一个事 ...

  6. day 21 01 序列化模块和模块的导入的复习以及包的初识

    day 21 01 序列化和模块的导入的复习以及包的初识 1.序列化模块 什么是序列化模块:数据类型转化成字符串的过程就是序列卷 为什么要使用序列化模块:为了方便存储和网络传输 三种序列化模块: (1 ...

  7. day21 01 包的初识

    day21 01包的初识 包:把解决一类问题的模块放在同一个文件夹里面-----包(一个包里面通常会含有_init_.py文件(python2里面必须有),但是后面的就没有要求一定要有了) 同样导入的 ...

  8. Fiddler-001-抓包工具初识

    Fiddler 是一个非常简单的网络调试器,也是目前最常用的http抓包工具之一 .通过 Fiddler,我们能够能够记录客户端和服务器之间的所有 HTTP请求,即记录并检查所有你的电脑和互联网之间的 ...

  9. 包的初识和进阶&异常处理

    包 包是一种通过使用‘.模块名’来组织python模块名称空间的方式. 1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警 ...

随机推荐

  1. RabbitMQ实战经验分享

    前言 最近在忙一个高考项目,看着系统顺利完成了这次高考,终于可以松口气了.看到那些即将参加高考的学生,也想起当年高三的自己. 下面分享下RabbitMQ实战经验,希望对大家有所帮助: 一.生产消息 关 ...

  2. Hello 2019 (D~G)

    目录 Codeforces 1097 D.Makoto and a Blackboard(DP 期望) E.Egor and an RPG game(思路 LIS Dilworth定理) F.Alex ...

  3. Java笔记(十七) 异步任务执行服务

    异步任务执行服务 一.基本原理和概念 一)基本接口 1)Runnable和Callable:表示要执行的异步任务. 2)Executor和ExecutorService:表示执行服务. 3)Futur ...

  4. 屏幕录制软件camtasia studio 8序列号激活

    注册名:TEAM MESMERiZE序列号:3MHCA-5DMCV-H89T8-V8GML-W6FB8 打开hosts文件:C:\Windows\System32\drivers\etc\hosts把 ...

  5. [P3385]【模板】负环 (spfa / bellman-ford)

    终于开始认真对待图论了 因为听说一直是提高组的,动得很少,直到现在机房打提高的氛围下,开始学一些皮毛的东西 模板题目链接 这是一道求负环的题目,照理来说大家都是用spfa来判断负环的 但是我觉得bel ...

  6. C#扩展方法实现

    C#提供了一种机制,可以扩展系统或者第三方类库中的方法.比如说想在string类型的对象里面多一个ToInt32(),来方便的将字符转换成整形.在实现的过程中的关键字为static和this即可.   ...

  7. Flask蓝图

    它的作用就是将 功能 与 主服务 分开怎么理解呢? 比如说,你有一个客户管理系统,最开始的时候,只有一个查看客户列表的功能,后来你又加入了一个添加客户的功能(add_user)模块, 然后又加入了一个 ...

  8. Java基础知识--内存管理

    Java语言中 的垃圾收集器相对于以前的其他语言优势是什么? 过去的语言需要程序员显示的进行分配内存.释放内存.这种做法可能会引起“内存泄漏”,即由于某种原因是分配给程序的内存无法释放,如果该任务不断 ...

  9. POJ1068 --(模拟)

    这题是在看一个书的时候的一个例题,当时并不明白啥意思,于是便找了下原题,以前没在POJ上刷过,这是开了个头,以后努力刷这个网站 题目大概意思是:http://poj.org/problem?id=10 ...

  10. JS自学笔记01

    JS自学笔记01 1.开发工具 webstorm 2.js(javascript) 是一门脚本.解释性.动态类型.基于对象的语言 含三个部分: ECMAScript标准–java基本语法 DOM(Do ...