英文:
Not getting expected output from goroutine
问题
我在(https://www.geeksforgeeks.org/channel-in-golang/)上读到了以下内容:
“在通道中,默认情况下,发送和接收操作会阻塞,直到另一侧准备好。它允许goroutine在没有显式锁或条件变量的情况下进行同步。”
为了测试上述说法,我编写了下面提到的示例程序:
程序:
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int) {
fmt.Println("在goroutine中:: myFunc()")
fmt.Println(10 + <-ch) // 根据规则,控制将在此处阻塞,直到'ch'发送一些数据,以便在我们的myFunc() goroutine中接收到它。
}
func main() {
fmt.Println("开始Main方法")
// 创建一个通道
ch := make(chan int)
go myFunc(ch) // 这个goroutine在一个新线程中启动
time.Sleep(2 * time.Second) // 引入2秒的休眠,以确保myFunc() goroutine在主线程之前执行
ch <- 10
fmt.Println("结束Main方法")
}
我期望的输出是:
开始Main方法
在goroutine中:: myFunc()
20
结束Main方法
但是,实际输出是:
开始Main方法
在goroutine中:: myFunc()
结束Main方法
为什么通过通道发送的值没有打印出来?
我认为这是因为主线程先完成了执行,因此所有其他的goroutine也终止了。
如果是这样的话,那么为什么规则说 - 它允许goroutine在没有显式锁或条件变量的情况下进行同步。
因为为了得到预期的输出,我必须使用sync.WaitGroup
来告诉主线程等待其他goroutine完成。这是否违反了上述规则,因为我在使用锁的形式waitgroup?
PS:我正在学习golang。所以如果我完全理解错误,请原谅。
英文:
I read it on (https://www.geeksforgeeks.org/channel-in-golang/) that:
"In the channel, the send and receive operation block until another side is not ready by default.
It allows goroutine to synchronize with each other without explicit locks or condition variables."
To test above statement, I have written a sample program mentioned below:
Program:
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int) {
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
go myFunc(ch) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
fmt.Println("End Main method")
}
I was expecting below output:
Start Main method
Inside goroutine:: myFunc()
20
End Main method
But, Actual output received is:
Start Main method
Inside goroutine:: myFunc()
End Main method
Why the value sent through channel is not printed?
I think, it is because main thread finished its execution first and hence, all other goroutine also terminated.
If that is the case, then, why does the rule said - It allows goroutine to synchronize with each other without explicit locks or condition variables.
Because, to get the expected output, I have to use sync.WaitGroup
to tell the main thread to wait for the other goroutine to finish. Isn't it violating the above rule as I am using locks in form of waitgroup?
PS: I am learning golang. So please forgive if I get the concept totally wrong.
答案1
得分: 1
主goroutine在myFunc
goroutine能够打印输出之前就已经退出了。下面是一个确保myFunc
goroutine在主goroutine退出之前完成的实现。
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("在goroutine中:: myFunc()")
fmt.Println(10 + <-ch) // 根据规则,控制将在此处阻塞,直到'ch'发送一些数据,以便在我们的myFunc() goroutine中接收到它。
}
func main() {
fmt.Println("开始Main方法")
// 创建一个通道
ch := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1)
go myFunc(ch, &wg) // 这个goroutine在一个新线程中启动
time.Sleep(2 * time.Second) // 引入2秒的延迟,以确保myFunc() goroutine在主线程之前执行
ch <- 10
wg.Wait()
fmt.Println("结束Main方法")
}
这里使用通道进行同步,它按照文档中描述的方式工作。这并不意味着从代码的这一点开始的代码将以相同的速度执行。它只意味着如果myFunc
goroutine没有从通道中读取数据,主goroutine将不会继续执行。而myFunc
将等待主goroutine将数据推送到通道中。在这种情况下,两个goroutine将独立地继续执行。
英文:
The main goroutine exists before the myFunc
goroutine is able to print the output. Here is an implementation which ensures that myFunc
goroutine finishes before the main goroutine exits.
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1)
go myFunc(ch, &wg) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
wg.Wait()
fmt.Println("End Main method")
}
The channels are used here for synchronization and it works as described in documentation. It does not mean that the code starting from this point in the code will be executed at the same speed. It only means that main goroutine will not continue if myFunc
goroutine is not reading from channel. And myFunc
will wait for main goroutine to push data to channel. After this happen both goroutines will continue it execution independently.
答案2
得分: 1
请尝试这样做,将你的代码作为基础:
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int, done chan struct{}) {
defer close(done) // 在函数退出阶段关闭通道
fmt.Println("在 goroutine 中:: myFunc()")
fmt.Println(10 + <-ch) // 根据规则,控制将在此处阻塞,直到 'ch' 发送一些数据,以便在我们的 myFunc() 协程中接收到它。
}
func main() {
fmt.Println("开始 Main 方法")
// 创建一个通道
ch := make(chan int)
done := make(chan struct{}) // 信号通道
go myFunc(ch, done) // 这个协程在一个新线程中启动
time.Sleep(2 * time.Second) // 引入 2 秒的休眠,以确保 myFunc() 协程在主线程之前执行
ch <- 10
<-done // 等待函数完成
fmt.Println("结束 Main 方法")
}
或者使用 Jaroslaw 的建议。
英文:
Try this, used your code as basis
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int, done chan struct{}) {
defer close(done) // channel will be closed in the function exit phase
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
done := make(chan struct{}) // signal channel
go myFunc(ch, done) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
<-done // waiting for function complete
fmt.Println("End Main method")
}
Or use Jaroslaw's suggestion.
答案3
得分: 0
因为Go语言非常快速... https://play.golang.org/p/LNyDAA3mGYY
当你发送到通道的调度器不够快时... 程序会退出。我引入了一个额外的上下文切换器来展示调度器的效果。
英文:
Because go is so fast... https://play.golang.org/p/LNyDAA3mGYY
After you send to channel scheduler isn't fast enoght... and program exists. I have introduced an additional context switcher for scheduler to show effect.
答案4
得分: 0
是的,你是对的。
如果你检查上面的程序执行,你会发现在主线程写入通道之前有一个睡眠操作。现在,尽管哪个goroutine会获得CPU时间是完全随机的,但在上述情况下,如果main
在explicit sleep
逻辑之前睡眠,myFunc
将被阻塞,因为ch
中没有数据。
在上面的代码中,我对代码进行了轻微的修改,使得main
在向通道写入数据之后睡眠。这样就可以得到预期的输出,而不使用waitgroup
或quit channels
。
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int) {
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) // 根据规则,控制将在此处阻塞,直到'ch'发送一些数据,以便在我们的myFunc() goroutine中接收到它。
}
func main() {
fmt.Println("Start Main method")
// 创建一个通道
ch := make(chan int)
go myFunc(ch) // 这个goroutine在一个新的线程中启动
ch <- 10
time.Sleep(2 * time.Second) // 引入2秒的睡眠,以确保myFunc() goroutine在主线程之前执行
fmt.Println("End Main method")
}
希望对你有帮助!
英文:
Yes, you are right
>I think, it is because main thread finished its execution first and hence, all other goroutine also terminated.
If you check the above program execution. The sleep is before main thread writes to the channel. Now even though which goroutine() will have CPU time is completely arbitary, but in the above case if the main
sleeps before the explicit sleep
logic. myFunc
will be blocked as there is no data in ch
Here I made a slight change to the above code to make main
sleep after writing data into Channel. It gives the expected output, Without using waitgroup
or quit channels
.
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int) {
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
go myFunc(ch) //<-- This go routine started in a new thread
ch <- 10
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
fmt.Println("End Main method")
}
答案5
得分: -1
目前存在一个竞争条件,即myFunc能够打印输出和你的主函数退出之间的竞争。
如果我们查看程序执行的规范,可以在以下链接找到:
https://golang.org/ref/spec#Program_execution
程序执行从初始化主包开始,然后调用main函数。当该函数调用返回时,程序退出。它不会等待其他(非主)goroutine 完成。
你仍然需要确保在主goroutine退出之前,所有生成的goroutine都能够完成任务。
在你的情况下,你可以使用一个waitgroup,就像你提到的那样,或者你可以使用一个done通道。
https://play.golang.org/p/RVr0HXuUMgn
根据你的代码,你也可以关闭用于发送整数的通道,因为你将其作为双向参数传递给函数,但这不是严格符合惯例的做法。
https://play.golang.org/p/wGvexC5ZgIi
英文:
It is currently a race condition between the myFunc being able to print and your main function exiting.
If we look at the spec for program execution at
https://golang.org/ref/spec#Program_execution
> Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
It is still your job to make sure that all spawned goroutines will complete before your main goroutine exists.
In your case, you could use a waitgroup as you mentioned or you could use a done channel.
https://play.golang.org/p/RVr0HXuUMgn
Given your code you could also close the channel you use to send the integer over since you are passing it to the function as bidirectional but it's not strictly idiomatic.
https://play.golang.org/p/wGvexC5ZgIi
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论