2016-10-06 3 views
3

Es ist eines der Beispiel-Code von Golang. Aber kann nicht verstehen, warum "fertig" Kanal in diesem Fall brauchen.Golang - Warum verwendet Kanal

https://gobyexample.com/closing-channels

Es gibt keinen Grund zu richten wahr gemacht Kanal. Wir können wissen, dass der Jobkanal fertig ist, wenn die Nachricht "Alle Jobs gesendet" gedruckt wird, oder?

Ich löschte Code, der relativ zum fertigen Kanal und Ergebnis ist immer noch gleich.

https://play.golang.org/p/xOmzobFpTQ

Antwort

4

das Ergebnis Nein ist nicht das gleiche:
Ihr Haupt goroutine Ausfahrten vor Ihren received job goroutine in vielen Situationen (zB verschiedene CPU-Last, und es ist nicht deterministisch und systemabhängig Verhalten), so auf diese Weise kann man nicht garantieren, dass all jobs received, zIn nur

time.Sleep(500) 

vor

fmt.Println("received job", j) 

dies zu sehen, versuchen Sie es auf The Go Playground:

// _Closing_ a channel indicates that no more values 
// will be sent on it. This can be useful to communicate 
// completion to the channel's receivers. 

package main 

import (
    "fmt" 
    "time" 
) 

// In this example we'll use a `jobs` channel to 
// communicate work to be done from the `main()` goroutine 
// to a worker goroutine. When we have no more jobs for 
// the worker we'll `close` the `jobs` channel. 
func main() { 
    jobs := make(chan int, 5) 
    //done := make(chan bool) 

    // Here's the worker goroutine. It repeatedly receives 
    // from `jobs` with `j, more := <-jobs`. In this 
    // special 2-value form of receive, the `more` value 
    // will be `false` if `jobs` has been `close`d and all 
    // values in the channel have already been received. 
    // We use this to notify on `done` when we've worked 
    // all our jobs. 
    go func() { 
     for { 
      j, more := <-jobs 
      if more { 
       time.Sleep(500) 
       fmt.Println("received job", j) 
      } else { 
       fmt.Println("received all jobs") 
       //done <- true 
       return 
      } 
     } 
    }() 

    // This sends 3 jobs to the worker over the `jobs` 
    // channel, then closes it. 
    for j := 1; j <= 3; j++ { 
     jobs <- j 
     fmt.Println("sent job", j) 
    } 
    close(jobs) 
    fmt.Println("sent all jobs") 

    // We await the worker using the 
    // [synchronization](channel-synchronization) approach 
    // we saw earlier. 
    //<-done 
} 

Ausgang:

sent job 1 
sent job 2 
sent job 3 
sent all jobs 

statt:

sent job 1 
received job 1 
received job 2 
sent job 2 
sent job 3 
received job 3 
received all jobs 
sent all jobs 

See:
Goroutine does not execute if time.Sleep included
Why is time.sleep required to run certain goroutines?
Weird channel behavior in go

+0

Es hat mir sehr geholfen. Danke :) –

2

TL; DR: Es gibt eine Race-Bedingung --- Du hast Glück. Wenn Sie den Kanal done nicht haben, ist die Ausgabe des Programms nicht deterministisch.

Abhängig von der Ausführungsreihenfolge des Threads kann der Hauptthread beendet werden, bevor die Goroutine ihre Verarbeitung beendet hat, wodurch die Goroutine in der Mitte abgebrochen wurde.

Durch Erzwingen des Hauptthreads aus dem done-Kanal erzwingen wir den Haupt-Thread warten, bis einige Daten im done-Kanal verbraucht werden. Dies gibt uns einen ordentlichen Synchronisationsmechanismus, bei dem die Goroutine den Hauptthread darüber informiert, dass dies geschieht, indem sie in den Kanal done schreibt. Dies führt dazu, dass der Hauptthread <- done blockiert und das Programm beendet wird.

1
  • Sent den Job nicht bedeutet getan wird, wenn der Job lange Finish nehmen.

  • Der Auftragskanal ist gepuffert, so dass selbst der Auftrag gesendet wird, der Auftrag kann vom Arbeiter noch nicht empfangen werden.

0

Ich denke, die Antwort akzeptiert, nicht in die genauen Gründe, warum ging.

Die Grundidee ist das, erinnert golang eine precedural Sprache ist, das heißt, jede intruction linear ausgeführt wird, dass Denken Sie daran, wenn ein Goroutine vom Haupt goroutine gegabelt ist, es basicallys auf seinem eigenen kleinen Abenteuer erlischt. Den Hauptthread zurücklassen. Der gepufferte Kanal hat eine Kapazität von 5, was bedeutet, dass er nicht blockiert, bis der Puffer voll ist, und blockiert, wenn er leer ist (beachte, dass ein Kanal mit einer Kapazität von Null natürlich ungepuffert ist). Also, weil es nur 4 Iteration (0 bis < = 3) wird es nicht blockieren. Barrowing diese Linie, den Haupt-Thread Durch Erzwingen von dem getan Kanal zu lesen, sind zwingen wir den Haupt-Thread zu warten, bis es einige Daten in dem getan Kanal verbraucht werden. Also, wenn die Iteration beendet ist, wird der else-Block ausgeführt, dann done <- true bewirkt, dass das Sperrgewinde <- done den nun eingefügten Wert aus dem done ziehen. Die Haupt-Goroutine ist nicht länger blockiert und wird daher erfolgreich beendet.

+0

Entschuldigung für das Mischen von Threads und Goroutines, sie sind nicht das Gleiche! – Remario

+0

so im Allgemeinen, da der Puffer ausreichend Kapazität hat, hat es einen Block nicht – Remario

+0

gepufferten Kanalblocks nur dann, wenn der Puffer voll ist. Empfängt Block, wenn der Puffer leer ist. – Remario

Verwandte Themen