Golang:与频道同时写作

时间:2016-05-19 02:23:11

标签: go concurrency channel

我写了一个简短的脚本来同时写文件。 一个goroutine应该将字符串写入文件,而其他人应该通过一个通道将消息发送给它。 但是,由于某些非常奇怪的原因,文件已创建,但没有通过频道向其添加消息。

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()
}

printOutput()goroutine是完全执行的,如果我试图在for循环之后写一些它实际上会进入文件。所以这让我认为范围输出可能为空

2 个答案:

答案 0 :(得分:1)

你需要从输出通道中取出一些东西,因为它会阻塞,直到某些东西移除你放在它上面的东西。

不是唯一/最好的方法,但是:我将printOutput()移到其他函数之上并将其作为go例程运行,它可以防止死锁。

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

答案 1 :(得分:1)

您获得空output的原因之一是因为频道对于发送/接收都是blocking

根据您的流程,下面的代码段永远不会达到wg.Done(),因为发送频道希望接收端将数据拉出。这是一个典型的死锁示例。

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

让我们来看看主要功能:

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