2012-11-28 10 views
36

Ich lerne Go und bin bisher sehr beeindruckt davon. Ich habe alle Online-Dokumente auf golang.org gelesen und bin in der Mitte von Chrisnalls "The Go Programming Language Phrasebook". Ich bekomme das Konzept der Kanäle und denke, dass sie sehr nützlich sein werden. Allerdings muss ich auf dem Weg etwas Wichtiges verpasst haben, da ich den Weg zu Einweg-Kanälen nicht sehen kann.Was ist der Sinn von Einweg-Kanälen in Go?

Wenn ich sie richtig interpretiere, kann ein schreibgeschützter Kanal nur empfangen werden und ein schreibgeschützter Kanal kann nur übertragen werden. Warum also einen Kanal, an den Sie senden können und auf dem Sie nie empfangen? Können sie von einer "Richtung" in die andere geworfen werden? Wenn ja, was ist der Punkt, wenn es keine tatsächliche Beschränkung gibt? Sind sie nichts anderes als ein Hinweis auf den Client-Code des Kanals?

Antwort

60

Ein schreibgeschützter Kanal kann für jeden, der ihn empfängt, schreibgeschützt gemacht werden, während der Sender immer noch einen Zweiwegkanal hat, auf den er schreiben kann. Zum Beispiel:

func F() <-chan int { 
    // Create a regular, two-way channel. 
    c := make(chan int) 

    go func() { 
     defer close(c) 

     // Do stuff 
     c <- 123 
    }() 

    // Returning it, implicitely converts it to read-only, 
    // as per the function return value. 
    return c 
} 

Wen immer F() nennt, erhält einen Kanal, auf dem sie nur lesen kann. Dies ist hauptsächlich nützlich, um mögliche Fehlverwendungen eines Kanals während der Kompilierung zu erfassen. Weil schreibgeschützte Kanäle unterschiedliche Typen sind, kann der Compiler seine vorhandenen Mechanismen zur Typprüfung verwenden, um sicherzustellen, dass der Aufrufer nicht versucht, Zeug in einen Kanal zu schreiben, an den er nicht schreibt.

+0

Aber auf der Empfängerseite kann der Anrufer es nicht zurück in zwei Richtungen werfen? Das ist etwas, woran ich hier herankomme. Es kann nur implizit konvertiert werden, indem es zurückgegeben wird? – burfl

+9

Sie können es nicht zurück in eine beschreibbare Version konvertieren. Der Compiler wird sich beschweren mit 'c kann nicht konvertiert werden c (geben Sie <-chan int) ein, um chan int einzugeben. Nun, Sie können das "unsichere" Paket verwenden, aber das ist eine ganz andere Geschichte. Alle Wetten sind deaktiviert, wenn dieses Paket ins Spiel kommt. – jimt

3

Go-Kanäle sind Hoare's Communicating Sequential Processes nachempfunden, einer Prozessalgebra für Parallelität, die sich an Ereignisflüssen zwischen kommunizierenden Akteuren orientiert (kleines 'a'). Als solche haben Kanäle eine Richtung, weil sie ein Sendeende und ein Empfangsende haben, d. H. Einen Erzeuger von Ereignissen und einen Verbraucher von Ereignissen. Ein ähnliches Modell wird auch in Occam und Limbo verwendet.

Dies ist wichtig - es wäre schwierig, über Deadlock-Probleme nachzudenken, wenn ein Kanalende beliebig zu verschiedenen Zeiten als Sender und Empfänger wiederverwendet werden könnte.

+0

Danke für das Hinzufügen. Ich finde das wertvoll. Vielleicht sollte dies eine neue Frage sein, aber ist es sicher für mehrere Absender, ein "Ende" und mehrere Empfänger zu teilen, um von dem anderen zu lesen? – burfl

+0

@burfl - Siehe meine Antwort. – tjameson

+0

Ich weiß nicht, ob mehrere Verfasser oder Leser von Go-Kanälen unterstützt werden. Eine der Antworten behauptet, dass dies der Fall ist, während die Sprachreferenz impliziert, dass dies nicht der Fall ist (aber ein wenig mehrdeutig ist). Es spielt kaum eine Rolle; Im Gegensatz dazu unterscheidet die JCSP-Java-Channel-Bibliothek zwischen Eins-zu-Eins- und Any-to-One-Kanälen (usw.), weil sie * zu * hat, während der Go-Compiler im Prinzip es automatisch ausarbeiten könnte. –

5

Ich denke, die Hauptmotivation für schreibgeschützte Kanäle ist die Verhinderung von Korruption und Paniken des Kanals. Stellen Sie sich vor, Sie könnten in den von time.After zurückgegebenen Kanal schreiben. Dies könnte eine Menge Code durcheinander bringen.

Auch kann panics auftreten, wenn Sie:

  • ein Kanal mehr schließen als einmal
  • Schreiben in einem geschlossenen Kanal

Diese Operationen Kompilierung-Fehler auftreten für Nur-Lese-Kanäle , aber sie können unangenehme Wettrennen verursachen, wenn mehrere Go-Routinen einen Kanal schreiben/schließen können.

Eine Möglichkeit, dies zu umgehen, ist es, niemals Kanäle zu schließen und sie Müll zu sammeln. Allerdings ist close nicht nur für die Bereinigung, aber es hat tatsächlich benutzen, wenn der Kanal reicht über ist:

func consumeAll(c <-chan bool) { 
    for b := range c { 
     ... 
    } 
} 

Wenn der Kanal nie geschlossen ist, wird diese Schleife nie enden. Wenn mehrere Go-Routinen in einen Kanal schreiben, dann gibt es eine Menge Buchführung, die weitergehen muss, um zu entscheiden, welcher Kanal den Kanal schließen wird.

Da Sie einen schreibgeschützten Kanal nicht schließen können, ist es einfacher, den richtigen Code zu schreiben.Wie @jimt in seinem Kommentar darauf hingewiesen hat, kann ein schreibgeschützter Kanal nicht in einen beschreibbaren Kanal umgewandelt werden, sodass Sie sicher sein können, dass nur Teile des Codes, die Zugriff auf die schreibbare Version eines Kanals haben, ihn schließen/schreiben können.

Edit:

Wie für mehrere Leser mit, das ist völlig in Ordnung, solange sie es erklären. Dies ist besonders nützlich, wenn es in einem Produzenten-/Verbrauchermodell verwendet wird. Zum Beispiel, sagen Sie einen TCP-Server, die Verbindungen nur akzeptiert und schreibt sie in eine Warteschlange für Worker-Threads:

func produce(l *net.TCPListener, c chan<- net.Conn) { 
    for { 
     conn, _ := l.Accept() 
     c<-conn 
    } 
} 

func consume(c <-chan net.Conn) { 
    for conn := range c { 
     // do something with conn 
    } 
} 

func main() { 
    c := make(chan net.Conn, 10) 
    for i := 0; i < 10; i++ { 
     go consume(c) 
    } 

    addr := net.TCPAddr{net.ParseIP("127.0.0.1"), 3000} 
    l, _ := net.ListenTCP("tcp", &addr) 
    produce(l, c) 
} 

wahrscheinlich Ihre Verbindung Handhabung wird länger dauern als eine neue Verbindung zu akzeptieren, so dass Sie viele haben wollen Verbraucher mit einem einzigen Produzenten. Mehrere Produzenten sind schwieriger (weil Sie koordinieren müssen, wer den Kanal schließt), aber Sie können dem Sendekanal einen Semaphor-ähnlichen Kanal hinzufügen.

+0

Vielen Dank! Viele gute Informationen hier. Ich weiß nicht, ob es die ursprüngliche Frage besser beantwortet als @jimt, also überlasse ich das als akzeptierte Antwort, aber das macht sehr gute Arbeit bei der Klärung nachfolgender Fragen, die ich in Kommentaren notiert habe. Ich werde definitiv upvote. – burfl

+0

Ich habe nicht erwartet, die angenommene Antwort zu erhalten =). Ich dachte nur, dass diese Information nützlich wäre. – tjameson