2014-09-04 9 views
21

Wie zu wissen, ein gepufferter Kanal ist voll? Ich weiß nicht, dass ich blockiert werden soll, wenn der gepufferte Kanal voll ist, stattdessen entscheide ich mich dafür, das Element, das an den gepufferten Kanal gesendet wurde, fallen zu lassen.Golang - Wie ein gepufferter Kanal ist voll

+0

Die Prämisse der Frage ist, dass du vermeiden willst, dass der Kanal jemals voll wird. Aber die Tatsache, dass Kanäle * sich synchronisieren können und daher beide Enden blockieren, ist ein wichtiger Teil des CSP-Denkens. Versuchen Sie nicht zu sehr zu verhindern, dass Ihre Puffer voll werden, bis Sie die Synchronisation besser verstehen. Versuchen Sie als Übung, mehrere Probleme nur mit * ungepufferten * Kanälen zu lösen. Dann können Sie sehen, wie das Hinzufügen von Pufferung im Nachhinein eine Leistungsverbesserung für ein bereits funktionierendes System sein kann. (Es gibt gelegentlich Fälle, in denen zu viel Pufferung sogar die Leistung verringern könnte.) –

Antwort

56

Sie können die select statement mit einem Standardwert verwenden. Falls es nicht möglich, jeden der Fälle zu tun, wie zu einem vollen Kanal zu senden, tut die Anweisung den Standard:

package main 

import "fmt" 

func main() { 
    ch := make(chan int, 1) 

    // Fill it up 
    ch <- 1 

    select { 
    case ch <- 2: // Put 2 in the channel unless it is full 
    default: 
     fmt.Println("Channel full. Discarding value") 
    } 
} 

Ausgang:

Kanal voll. Wegwerfen Wert

Spielplatz:http://play.golang.org/p/1QOLbj2Kz2

prüfen ohne

Sendet Es ist auch möglich, die Anzahl der Elemente in einem Kanal unter Verwendung len(ch) Warteschlangen zu überprüfen, wie sie in den Go specifications angegeben. In Verbindung mit cap können wir überprüfen, ob ein Kanal voll ist, ohne Daten zu senden.

if len(ch) == cap(ch) { 
    // Channel was full, but might not be by now 
} else { 
    // Channel wasn't full, but might be by now 
} 

Beachten Sie, dass das Ergebnis des Vergleichs durch die Zeit ungültig werden kann, geben Sie den if Block

+0

Aber was, wenn man nur überprüfen möchte, ob der Puffer voll ist, _nicht_ daran schreiben, auch wenn es nicht ist? Gibt es eine Möglichkeit, das zu tun? – Tom

+9

@Tom Sie können tatsächlich testen, ob len (ch) == cap (ch) {...} 'wo' len (ch) 'ist die Anzahl der Elemente im Kanal und' cap (ch) 'ist die Kapazität . Es ist jedoch möglicherweise ungültig, wenn Sie den if-Block eingeben. – ANisus

+0

groß - jetzt ist das eine komplette Antwort! – Tom

10

stattdessen ich wähle das Element in dem gepufferten Kanal gesendet fallen zu lassen.

, dass "überquellenden Kanal" genannt wird, und Sie finden Anisus Antwort implementiert in eapache/channels/overflowing_channel.go:

for elem := range ch.input { 
    // if we can't write it immediately, drop it and move on 
    select { 
    case ch.output <- elem: 
    default: 
    } 
} 
close(ch.output) 

Aber das Projekt eapache/channels implementiert andere Strategien als auch:

  • OverflowingChannel implementiert die Channel Schnittstelle auf eine Weise, die den Schreiber niemals blockiert.
    Wenn ein Wert in einen OverflowingChannel geschrieben wird, wenn sein Puffer voll ist
    (oder in einem ungepufferten Fall, wenn der Empfänger nicht bereit ist), dann wird dieser Wert einfach verworfen.

Für das entgegengesetzte Verhalten (das älteste Element verwirft, nicht die neueste) sehen RingChannel.

+1

Eine gute Antwort. Überlaufende Kanäle, die entweder das älteste oder das neueste Element verlieren, können manchmal wichtige Werkzeuge in Ihrer Toolbox sein. Betrachten Sie einen Ring von Goroutines: normalerweise gibt jede Schleife (a.k.a * cycle *) das Risiko eines Deadlocks. Das Ändern eines der Kanäle zu einem Überlauf behebt dieses Problem. Einige Ereignisse zu verlieren ist nicht wichtig, wenn sie die Art sind, die veraltet werden kann, können leicht ersetzt werden. Es gibt (natürlich) andere Möglichkeiten, das gleiche Deadlock-Problem zu beheben. –

1

Ein anderes nützliches Beispiel, über das ich gestolpert bin, war this nifty implementation von Ring Buffer.

Das Zitat aus der Quelle:

Die Idee ist einfach: Verbinden zwei gepufferte Kanäle durch eine Goroutine, die Nachrichten aus dem Eingangskanal zu dem abgehenden Kanal weiterleitet. Wenn eine neue Nachricht nicht auf dem ausgehenden Kanal platziert werden kann, nehmen Sie eine Nachricht aus dem ausgehenden Kanal ( ist die älteste Nachricht im Puffer), legen Sie sie ab und legen Sie die neue Nachricht in den neuen freigegebenen Ausgang Kanal.

Check out this C version auch ...

0

Verwenden len(channel) zu überprüfen, wie viele Elemente zur Zeit im Kanal ist und vergleichen Sie das mit cap(channel), seine Kapazität:

func TestChannelFull(t *testing.T) { 
    chansize := 100 
    ch := make(chan bool, chansize) 
    t.Log(cap(ch)) 
    t.Log(len(ch)) 
    for len(ch) < cap(ch) { 
     ch <- true 
    } 
    t.Log(len(ch)) 
    t.Log("quit because channel is full") 
} 

init_test.go:25: 100 
init_test.go:26: 0 
init_test.go:30: 100 
init_test.go:31: quit because channel is full 
PASS