为了账号安全,请及时绑定邮箱和手机立即绑定

与频道同时写入

与频道同时写入

Go
炎炎设计 2022-01-10 17:14:57
我写了一个简短的脚本来同时写一个文件。一个 goroutine 应该将字符串写入文件,而其他 goroutine 应该通过通道将消息发送给它。但是,由于某些非常奇怪的原因,创建了文件,但没有通过通道向其中添加任何消息。package mainimport (    "fmt"    "os"    "sync")var wg sync.WaitGroupvar 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()够程被完全执行,如果我试图写的for循环它实际上进入文件之后的东西。所以这让我认为范围输出可能为空
查看完整描述

3 回答

?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

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


不是唯一/最好的方法,但是:我移到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)

}


查看完整回答
反对 回复 2022-01-10
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

一,为什么你会得到一个空的原因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()

}


查看完整回答
反对 回复 2022-01-10
?
蝴蝶刀刀

TA贡献1801条经验 获得超8个赞

我对这个问题的看法:



package main


import (

    "fmt"

    "os"

    "sync"

)


var wg sync.WaitGroup

var output = make(chan string)

var donePrinting = make(chan struct{})


func concurrent(n uint) {

    defer wg.Done() // It only makes sense to defer

     // wg.Done() before you do something.

     // (like sending a string to the output channel)

    output <- fmt.Sprint(n)

}


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

    }

    donePrinting <- struct{}{}

}


func main() {

    wg.Add(2)

    go printOutput()

    go concurrent(1)

    go concurrent(2)

    wg.Wait()

    close(output)

    <-donePrinting

}

每个concurrent函数将从等待组中扣除。


两个concurrentgoroutine 完成后,wg.Wait()将解除阻塞,并执行下一条指令 ( close(output))。在关闭通道之前,您必须等待两个 goroutine 完成。相反,如果您尝试以下操作:


go printOutput()

go concurrent(1)

go concurrent(2)

close(output)

wg.Wait()

您可能会close(output)在任何一个concurrentgoroutine 结束之前执行指令。如果通道在并发 goroutine 运行之前关闭,它们将在运行时崩溃(在尝试写入关闭的通道时)。


那么,如果您不等待printOutput()goroutine 完成,您实际上可以main()在printOutput()有机会完成对其文件的写入之前退出。


因为我想printOutput()在退出程序之前等待goroutine 完成,所以我还创建了一个单独的通道来表示printOutput()已经完成。


的<-donePrinting指令块,直到main接收的东西在donePrinting信道。一旦main收到任何东西(甚至是printOutput()发送的空结构),它就会解除阻塞并运行到结论。


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


查看完整回答
反对 回复 2022-01-10
  • 3 回答
  • 0 关注
  • 152 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号