chan(golang)的N种玩法

调用一次以上就panic

一个稍微大一点的工程,初始化函数和反初始化函数一遍只允许调用一次,调动超过一次可能会造成不可想象的问题。出于简单考虑,此时只要调用超过一次以上就panic。实现这个功能非常用chan非常容易,如下代码所示:

// 定义一个只调用一次的变量
var once = make(chan struct{})

// 以全局只能关闭一次为例
func Close() {
    // 在执行关闭前先关闭chan,因为chan关闭两次就会panic
    close(once)
}

这种实现方法最大的特点就是简单,总共分两步:1.申请一个chan变量;2.需要时close()这个chan。过程中没有任何判断,代码显得比较优雅。当然,它和下面的实现方法是一样的:

// 定义一个int32类型的变量
var once int32

func Close() {
    // 利用原子的方式竞争,竞争失败的panic
    if !atomic.CompareAndSwapInt32(&once, 0, 1) {
        panic("Close once")
    }
}

这种方法虽然和chan的结果一样,但是代码就没那么简洁,至少多了一个判断。相信有同学不屑于这种实现方法,用sync.Once让函数只会执行一次,不仅能达到效果,关键还能避免panic。其实panic的目标在于调试,为了尽快发现不符合预期的bug,等到发布或者上线时相信已经基本不可能触发panic了。

多生产单消费Condition

Condition是一种比较常用的同步类型,比如C++中的std::condition_variable或者golang中的sync.Cond,但他们都需要锁来配合使用。而用chan实现的Condition不需要配合锁使用,但是只能有一个消费者,因为它无法实现Broadcast功能。

// 用chan定义Cond类型
type Cond chan struct{}

// Cond构造函数,注意chan的buffer大小为1,这是用chan实现Cond的关键
func NewCond() Cond {
    return make(Cond, 1)
}

// 等待
func (c Cond) Wait() {
    <-c
}

// 超时等待
func (c Cond) WaitTimeout(d time.Duration) bool {
    select {
    case <-c:
        return true
    case <-time.After(d):
        return false
    }
}

// 通知
func (c Cond) Signal() {
    select {
    case c <- struct{}{}:
    // default很重要,否则可能会被阻塞
    default:
    }
}

对于多生产单消费的场景还是比较普遍的,所以用chan实现的Condition可以让这种场景的代码变得更加简洁和优雅,毕竟无需锁的配合。chan的buffer大小为1是实现Condition的关键,其中的原理读者应该很容易理解。

栅栏Barrier

关于实现栅栏笔者在几年前写一个文章《golang的chan有趣用法》,有兴趣的读者可以看一看。有读者反馈sync.Group它不香么?此处我需要说明一下,题目要求就是如何用chan实现栅栏,考验的是对chan的理解和应用。再者sync.Group有一个天然的缺点,就是没有超时等待,而用chan实现的栅栏是可以实现超时等待的。这也是本文提及chan实现栅栏的关键所在,毕竟在原文中没有提到超时等待。

面向活体的编程

面向活体的编程充分利用了golang的chan与协程,每个对象都有一个协程函数处理所有的接口请求,接口的调用都会转换为向某个chan发送参数。这种编程模式的典型代表就是ecd的raft包,感兴趣的读者可以阅读笔者的《etcd的raft实现之node》。笔者之所以称之为面向活体的编程,是因为这种编程模式在面向对象的编程基础上赋予了对象生命,即它的协程函数。活体之间通过chan交互,这是不是现实中人的写照?一个人接受来自各个chan(比如视觉、听觉、触觉、微信、钉钉、电话等)的请求,然后做出响应。

每一中编程模式并不是代表了什么先进的技术,比如面向对象的编程并没有比面向过程的编程先进,只代表了抽象事物与问题的方法。虽然面向活体的编程本质上与面向对象的编程+线程池有什么较大的区别,但是这种编程模式中对象都是活的,这是方法论的不同。就要像C的结构体加函数与C++也没啥不同一样,纠结于这一点思维有点过于局限了。

猜你喜欢

转载自blog.csdn.net/weixin_42663840/article/details/113856453