2016-05-19 6 views
1

Ich schrieb ein kurzes Skript, um Dateien gleichzeitig zu schreiben. Eine Goroutine soll Strings in eine Datei schreiben, während die anderen die Nachrichten über einen Kanal dorthin senden sollen. Aus irgendeinem wirklich seltsamen Grund wird die Datei erstellt, aber über den Kanal wird keine Nachricht hinzugefügt.Golang: Schreiben gleichzeitig mit Kanälen

package main 

import (
    "fmt" 
    "os" 
    "sync" 
) 

var wg sync.WaitGroup 
var output = make(chan string) 

func concurrent(n uint64) { 
    output <- fmt.Sprint(n) 
    defer wg.Done() 
} 

func printOutput() { 
    f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666); 
    if err != nil { 
      panic(err) 
    } 
    defer f.Close() 

    for msg := range output { 
      f.WriteString(msg+"\n") 
    } 
} 

func main() { 
    wg.Add(2) 
    go concurrent(1) 
    go concurrent(2) 
    wg.Wait() 
    close(output) 
    printOutput() 
} 

Die printOutput() goroutine vollständig ausgeführt, wenn ich etwas zu schreiben versucht, nach dem for-Schleife würde es tatsächlich in die Datei. Das führt mich zu der Annahme, dass die Bereichsausgabe Null sein könnte.

+0

Können Sie das Codebeispiel reparieren? Wie geschrieben, würde es nicht laufen. – Snowman

+0

@Snowman Sicher! Gib mir einen Moment, ich werde die Typvariable ändern – Juanvulcano

+1

@Snowman Sollte jetzt viel besser sein, https://play.golang.org/p/Cx6mcUHiSU – Juanvulcano

Antwort

1

Einer der Gründe, warum Sie eine Null bekommen output ist, weil Kanäle sind blocking für beide senden/empfangen.

Je nach Ihrem Fluss erreicht der Code-Ausschnitt unten niemals wg.Done(), da der Sendekanal ein Empfangsende erwartet, um die Daten herauszuziehen. Dies ist ein typisches Deadlock-Beispiel.

func concurrent(n uint64) { 
    output <- fmt.Sprint(n) // go routine is blocked until data in channel is fetched. 
    defer wg.Done() 
} 

Lassen Sie uns die Haupt func untersuchen:

func main() { 
    wg.Add(2) 
    go concurrent(1) 
    go concurrent(2) 
    wg.Wait()  // the main thread will be waiting indefinitely here. 
    close(output) 
    printOutput() 
} 

+0

Schließen des Kanals am Ende der Go-Routine funktioniert nicht? – Juanvulcano

+0

@Juanvulcano Nein, da diese Zeile niemals ausgeführt wird. – Roylee

+0

@Juanvulcano Schließen Sie den Kanal nach 'wg.Wait()' trotzdem. 'wg.Done()' wird nicht erreicht, weil 'output <- fmt.Sprint (n)' Blöcke auf 'printOutput() 'warten, um den Wert zu entfernen. Und du kommst nie zu 'printOutput()', weil es auch nach 'wg.Wait()' steht. Also wartest du permanent auf 'wg.Done()' – Snowman

1

Sie müssen etwas vom Ausgangskanal nehmen, da es blockiert, bis etwas entfernt, was Sie darauf setzen.

Nicht der einzige/beste Weg, es zu tun, aber: Ich bewegte printOutput() über die anderen Funktionen und führen Sie es als eine Go-Routine und es verhindert den Deadlock.

package main 

import (
    "fmt" 
    "os" 
    "sync" 
) 

var wg sync.WaitGroup 
var output = make(chan string) 

func concurrent(n uint64) { 
    output <- fmt.Sprint(n) 
    defer wg.Done() 
} 

func printOutput() { 
    f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666) 
    if err != nil { 
     panic(err) 
    } 
    defer f.Close() 

    for msg := range output { 
     f.WriteString(msg + "\n") 
    } 
} 

func main() { 
    go printOutput() 
    wg.Add(2) 
    go concurrent(1) 
    go concurrent(2) 
    wg.Wait() 
    close(output) 
}