在go中使用linked channels進行資料廣播

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

在go中使用linked channels進行資料廣播

原文在這裡(需翻牆),為啥想要翻譯這篇文章是因為在實際中也碰到過如此的問題,而該文章的解決方式很巧妙,希望對大家有用。

在go中channels是一個很強大的東西,但是在處理某些事情上面還是有局限的。其中之一就是一對多的通訊。channels在多個writer,一個reader的模型下面工作的很好,但是卻不能很容易的處理多個reader等待擷取一個writer發送的資料的情況。

處理這樣的情況,可能的一個go api原型如下:

type Broadcaster …func NewBroadcaster() Broadcasterfunc (b Broadcaster) Write(v interface{})func (b Broadcaster) Listen() chan interface{}

broadcast channel通過NewBroadcaster建立,通過Write函數進行資料廣播。為了監聽這個channel的資訊,我們使用Listen,該函數返回一個新的channel去接受Write發送的資料。

這套解決方案需要一個中間process用來處理所有reader的註冊。當調用Listen建立新的channel之後,該channel就被註冊,通常該中間process的主迴圈如下:

for {    select {        case v := <-inc:            for _, c := range(listeners) {                c <- v            }        case c := <- registeryc:            listeners.push(c)    }}

這是一個通常的做法,(譯者也經常這麼幹)。但是該process在處理資料廣播的時候會阻塞,直到所有的readers讀取到值。一個可選的解決方式就是reader的channel是有buffer緩衝的,緩衝大小我們可以按需調節。或者當buffer滿的時候我們將資料丟棄。

但是這篇blog並不是介紹上面這套做法的。這篇blog主要提出了另一種實現方式用來實現writer永遠不阻塞,一個慢的reader並不會因為writer發送資料太快而要考慮分配太大的buffer。

雖然這麼做不會有太高的效能,但是我並不在意,因為我覺得它很酷。我相信我會找到一個很好的使用地方的。

首先是核心的東西:

type broadcast struct {    c chan broadcast    v interface{}}

這就是我說的linked channel,但是其實是Ouroboros data structure。也就是,這個struct執行個體在發送到channel的時候包含了自己。

從另一方面來說,如果我有一個chan broadcast類型的資料,那麼我就能從中讀取一個broadcast b,b.v就是writer發送的任意資料,而b.c,這個原先的chan broadcast,則能夠讓我重複這個過程。

另一個可能讓人困惑的地方在於一個帶有緩衝區的channel能夠被用來當做一個1對多廣播的對象。如果我定義如下的buffered channel:

var c = make(chan T, 1)

任何試圖讀取c的process都將阻塞直到有資料寫入。

當我們想廣播一個資料的時候,我們只是簡單的將其寫入這個channel,這個值只會被一個reader給擷取,但是我們約定,只要讀取到了資料,我們立刻將其再次放入該channel,如下:

func wait(c chan T) T {    v := <-c    c <-v    return v}

結合上面兩個討論的東西,我們就能夠發現如果在broadcast struct裡面的channel如果能夠按照上面的方式進行處理,我們就能實現一個資料廣播。

代碼如下:

package broadcasttype broadcast struct {    c   chan broadcast;    v   interface{};}type Broadcaster struct {    // private fields:    Listenc chan chan (chan broadcast);    Sendc   chan<- interface{};}type Receiver struct {    // private fields:    C chan broadcast;}// create a new broadcaster object.func NewBroadcaster() Broadcaster {    listenc := make(chan (chan (chan broadcast)));    sendc := make(chan interface{});    go func() {        currc := make(chan broadcast, 1);        for {            select {            case v := <-sendc:                if v == nil {                    currc <- broadcast{};                    return;                }                c := make(chan broadcast, 1);                b := broadcast{c: c, v: v};                currc <- b;                currc = c;            case r := <-listenc:                r <- currc            }        }    }();    return Broadcaster{        Listenc: listenc,        Sendc: sendc,    };}// start listening to the broadcasts.func (b Broadcaster) Listen() Receiver {    c := make(chan chan broadcast, 0);    b.Listenc <- c;    return Receiver{<-c};}// broadcast a value to all listeners.func (b Broadcaster) Write(v interface{})   { b.Sendc <- v }// read a value that has been broadcast,// waiting until one is available if necessary.func (r *Receiver) Read() interface{} {    b := <-r.C;    v := b.v;    r.C <- b;    r.C = b.c;    return v;}

下面就是譯者的東西了,這套方式實現的很巧妙,首先它解決了reader register以及unregister的問題。其次,我覺得它很好的使用了流式化處理的方式,當reader讀取到了一個值,reader可以將其傳遞給下一個reader繼續使用,同時自己在開始監聽下一個新的值的到來。

譯者自己的一個測試案例:

func TestBroadcast(t *testing.T) {    b := NewBroadcaster()    r := b.Listen()    b.Write("hello")    if r.Read().(string) != "hello" {        t.Fatal("error string")    }    r1 := b.Listen()    b.Write(123)    if r.Read().(int) != 123 {        t.Fatal("error int")    }    if r1.Read().(int) != 123 {        t.Fatal("error int")    }    b.Write(nil)    if r.Read() != nil {        t.Fatal("error nit")    }    if r1.Read() != nil {        t.Fatal("error nit")    }}

當然,這套方式還有點不足,主要就在於Receiver Read函數,並不能很好的與select進行整合,具體可以參考該作者另一篇bloghttp://rogpeppe.wordpress.com/2010/01/04/select-functions-for-go/。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.