与通道同时写入
Writing concurrently with channels
我写了一个简短的脚本来并发写入一个文件。
一个 goroutine 应该将字符串写入文件,而其他 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 循环之后写一些东西,它实际上会进入文件。所以这让我认为范围输出可能为 null
你需要从输出通道中取出一些东西,因为它是阻塞的,直到有东西移除你放在它上面的东西。
不是 only/best 的方法,而是:我将 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)
}
您得到空 output
的原因之一是因为 channels 对于两个 send/receive 都是 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()
}
我对这个问题的看法:
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
函数将从等待组中扣除。
两个concurrent
goroutine完成后,wg.Wait()
会解除阻塞,执行下一条指令(close(output)
)。在关闭通道之前,您必须等待两个 goroutine 完成。相反,如果您尝试以下操作:
go printOutput()
go concurrent(1)
go concurrent(2)
close(output)
wg.Wait()
你可能会在任何 concurrent
goroutines 结束之前执行 close(output)
指令。如果通道在并发 goroutines 运行 之前关闭,它们将在 运行 时崩溃(同时尝试写入已关闭的通道)。
如果你不等待 printOutput()
goroutine 完成,你实际上可以在 printOutput()
有机会完成写入之前退出 main()
文件。
因为我想在退出程序之前等待 printOutput()
goroutine 完成,所以我还创建了一个单独的通道来表示 printOutput()
已完成。
<-donePrinting
指令阻塞,直到 main
通过 donePrinting
通道接收到一些东西。
一旦 main
收到任何东西(即使是 printOutput()
发送的空结构),它将解除阻塞并 运行 结论。
我写了一个简短的脚本来并发写入一个文件。 一个 goroutine 应该将字符串写入文件,而其他 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 循环之后写一些东西,它实际上会进入文件。所以这让我认为范围输出可能为 null
你需要从输出通道中取出一些东西,因为它是阻塞的,直到有东西移除你放在它上面的东西。
不是 only/best 的方法,而是:我将 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)
}
您得到空 output
的原因之一是因为 channels 对于两个 send/receive 都是 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()
}
我对这个问题的看法:
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
函数将从等待组中扣除。
两个concurrent
goroutine完成后,wg.Wait()
会解除阻塞,执行下一条指令(close(output)
)。在关闭通道之前,您必须等待两个 goroutine 完成。相反,如果您尝试以下操作:
go printOutput()
go concurrent(1)
go concurrent(2)
close(output)
wg.Wait()
你可能会在任何 concurrent
goroutines 结束之前执行 close(output)
指令。如果通道在并发 goroutines 运行 之前关闭,它们将在 运行 时崩溃(同时尝试写入已关闭的通道)。
如果你不等待 printOutput()
goroutine 完成,你实际上可以在 printOutput()
有机会完成写入之前退出 main()
文件。
因为我想在退出程序之前等待 printOutput()
goroutine 完成,所以我还创建了一个单独的通道来表示 printOutput()
已完成。
<-donePrinting
指令阻塞,直到 main
通过 donePrinting
通道接收到一些东西。
一旦 main
收到任何东西(即使是 printOutput()
发送的空结构),它将解除阻塞并 运行 结论。