引言
如何優(yōu)雅的同步化異步代碼嫁盲,一直以來(lái)都是各大編程語(yǔ)言致力于優(yōu)化的點(diǎn)叛本,記得最早是C# 5.0加入了async/await來(lái)簡(jiǎn)化TPL的多線程模型沪蓬,后來(lái)Javascript的Promise也吸取這一語(yǔ)法糖,在ES 6中也加入了async和await.
那么来候,被大家一稱贊并發(fā)性能好、異步模型獨(dú)樹(shù)一幟的golang吠勘,能否也有async和await呢性芬?
其實(shí),這對(duì)于golang的CSM來(lái)說(shuō)一點(diǎn)也不難剧防!
核心代碼如下:
done := make(chan struct{})
go func() {
// do work asynchronously here
//
close(done)
}()
<-done
是不是很簡(jiǎn)單呢植锉? go rountine負(fù)責(zé)async, channel的負(fù)責(zé)await, 簡(jiǎn)直是完美峭拘!
但這個(gè)代碼看起來(lái)還是有點(diǎn)丑俊庇,而且這個(gè)go func(){}
還沒(méi)有返回值狮暑,雖說(shuō)可以通過(guò)閉包來(lái)接收返回值,但那個(gè)代碼就更難維護(hù)了辉饱。
Go Promise
代碼難看不要緊搬男,只要Don't repeat yourself (DRY),封裝一下不就好了彭沼?
type WorkFunc func() (interface{}, error)
func NewPromise(workFunc WorkFunc) *Promise {
promise := Promise{done: make(chan struct{})}
go func() {
defer close(promise.done)
promise.res, promise.err = workFunc()
}()
return &promise
}
func (p *Promise) Done() (interface{}, error) {
<-p.done
return p.res, p.err
}
調(diào)用的代碼如下:
promise := NewPromise(func() (interface{}, error) {
// do work asynchronously here
//
return res, err
})
// await
res, err := promise.Done()
是不是美觀了許多呢缔逛?
這個(gè)實(shí)現(xiàn)和Javascript的Promise的API是有很大差距,使用體驗(yàn)上因?yàn)間olang沒(méi)有泛型姓惑,也需要轉(zhuǎn)來(lái)轉(zhuǎn)去的褐奴,但為了不辜負(fù)Promise這個(gè)名字,怎么能沒(méi)有then
呢于毙?
type SuccessHandler func(interface{}) (interface{}, error)
type ErrorHandler func(error) interface{}
func (p *Promise) Then(successHandler SuccessHandler, errorHandler ErrorHandler) *Promise {
newPromise := &Promise{done: make(chan struct{})}
go func() {
res, err := p.Done()
defer close(newPromise.done)
if err != nil {
if errorHandler != nil {
newPromise.res = errorHandler(err)
} else {
newPromise.err = err
}
} else {
if successHandler != nil {
newPromise.res, newPromise.err = successHandler(res)
} else {
newPromise.res = res
}
}
}()
return newPromise
}
有了then
可以chain起來(lái)敦冬,是不是找到些Promise的感覺(jué)呢?
完整代碼請(qǐng)查看 promise.go
Actor
本來(lái)我的理解也就到些了唯沮,然后前段時(shí)間(說(shuō)來(lái)也是一月有余了)脖旱,看了Go并發(fā)設(shè)計(jì)模式之 Active Object這篇文章后, 發(fā)現(xiàn)如果有一個(gè)常駐協(xié)程在異步的處理任務(wù)介蛉,而且是FIFO的萌庆,那么這其實(shí)是相當(dāng)于一個(gè)無(wú)鎖的設(shè)計(jì),可以簡(jiǎn)化對(duì)臨界資源的操作币旧。
于是踊兜,我照著文章的思路,實(shí)現(xiàn)了下面的代碼:
// Creates a new actor
func NewActor(setActorOptionFuncs ...SetActorOptionFunc) *Actor {
actor := &Actor{buffer: runtime.NumCPU(), quit: make(chan struct{}), wg: &sync.WaitGroup{}}
for _, setOptionFunc := range setActorOptionFuncs {
setOptionFunc(actor)
}
actor.queue = make(chan request, actor.buffer)
actor.wg.Add(1)
go actor.schedule()
return actor
}
// The long live go routine to run.
func (actor *Actor) schedule() {
loop:
for {
select {
case request := <-actor.queue:
request.promise.res, request.promise.err = request.work()
close(request.promise.done)
case <-actor.quit:
break loop
}
}
actor.wg.Done()
}
// Do a work.
func (actor *Actor) Do(workFunc WorkFunc) *Promise {
methodRequest := request{work: workFunc, promise: &Promise{
done: make(chan struct{}),
}}
actor.queue <- methodRequest
return methodRequest.promise
}
// Close actor
func (actor *Actor) Close() {
close(actor.quit)
actor.wg.Wait()
}
一個(gè)簡(jiǎn)單的沒(méi)啥意義的純粹為了demo的測(cè)試用例如下:
func TestActorAsQueue(t *testing.T) {
actor := NewActor()
defer actor.Close()
i := 0
workFunc := func() (interface{}, error) {
time.Sleep(1 * time.Second)
i++
return i, nil
}
promise := actor.Do(workFunc)
promise2 := actor.Do(workFunc)
res2, _ := promise2.Done()
res1, _ := promise.Done()
if res1 != 1 {
t.Fail()
}
if res2 != 2 {
t.Fail()
}
}
完整代碼請(qǐng)查看 actor.go
總結(jié)
每個(gè)語(yǔ)言都有它的獨(dú)特之處佳恬,在我的理解中捏境,玩轉(zhuǎn)golang的CSM模型,channel一定要用的6毁葱。
于是垫言,我創(chuàng)建了Channelx這個(gè)repo, 包含了對(duì)channel常用場(chǎng)景的封裝,歡迎大家審閱倾剿,喜歡的就點(diǎn)個(gè)star筷频。
此系列其它文章: