Golang:与频道并发

I wrote a short script to write files concurrently. One goroutine is supposed to write strings to a file while the others are supposed to send the messages through a channel to it. However, for some really strange reason the file is created but no message is added to it through the channel.

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+"
")
    }
}

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

The printOutput() goroutine is executed completely, if I tried to write something after the for loop it would actually get into the file. So this leads me to think that range output might be null

One of the reason why you get a null output is because channels are blocking for both send/receive.

According to your flow, the code snippet below will never reach wg.Done(), as sending channel is expecting a receiving end to pull the data out. This is a typical deadlock example.

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

Let's examine the main func:

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

You need to have something taking from the output channel as it is blocking until something removes what you put on it.

Not the only/best way to do it but: I moved printOutput() to above the other funcs and run it as a go routine and it prevents the 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 + "
")
    }
}

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