您當前的位置:首頁 > 書法

如何把golang的Channel玩出async和await的feel

作者:由 Java天天 發表于 書法時間:2020-02-27

如何把golang的Channel玩出async和await的feel

引言

如何優雅的同步化

非同步程式碼

,一直以來都是各大程式語言致力於最佳化的點,記得最早是C# 5。0加入了async/await來簡化TPL的

多執行緒

模型,後來Javascript的Promise也吸取這一語法糖,在ES 6中也加入了async和await。

那麼,被大家一稱讚併發效能好、非同步模型獨樹一幟的golang,能否也有async和await呢?

其實,這對於golang的CSM來說是一點也不難!

核心程式碼如下:

done := make(chan struct{})

go func() {

// do work asynchronously here

//

close(done)

}()

<-done

是不是很簡單呢?

go rountine

負責async, channel的負責await, 簡直是完美!

但這個程式碼看起來還是有點醜,而且這個go func(){}還沒有返回值,雖說可以透過閉包來接收

返回值

,但那個程式碼就更難維護了。

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

}

呼叫的程式碼如下:

promise := NewPromise(func() (interface{}, error) {

// do work asynchronously here

//

return res, err

})

// await

res, err := promise。Done()

是不是美觀了許多呢?

這個實現和Javascript的Promise的API是有很大差距,使用體驗上因為golang沒有泛型,也需要轉來轉去的,但為了不辜負Promise這個名字,怎麼能沒有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起來,是不是找到些Promise的感覺呢?

完整程式碼請檢視

promise。go

Actor

本來我的理解也就到些了,然後前段時間(說來也是一月有餘了),看了Go併發設計模式之 Active Object這篇文章後, 發現如果有一個常駐協程在非同步的處理任務,而且是FIFO的,那麼這其實是相當於一個無鎖的設計,可以簡化對臨界資源的操作。

於是,我照著文章的思路,實現了下面的程式碼:

// 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()

}

一個簡單的沒啥意義的純粹為了demo的測試用例如下:

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()

}

}

完整程式碼請檢視 actor。go(

https://

github。com/Ksloveyuan/c

hannelx/blob/master/actor。go

總結

每個語言都有它的獨特之處,在我的理解中,玩轉golang的CSM模型,channel一定要用的6。

最後:

如何把golang的Channel玩出async和await的feel

上面都是自己整理好的!我就把資料貢獻出來給有需要的人!順便求一波關注。

學習我們是認真的,拿大廠offer是勢在必得的。java(想了解更多點一下哦)

作者:raining1127

連結:

https://

juejin。im/post/5e4175a3

6fb9a07ca80a9c77

標簽: actor  func  promise  err