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.
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
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