Golang Go语言协程执行问题请教

Golang Go语言协程执行问题请教
package main

import (
“fmt”
“time”
)

var x = 0

func main() {
var num = 123
var p = &num
c := make(chan int)
go func() {
c <- *p + x // 1. 返回 123
// c <- *p // 2. 返回 789
}()
time.Sleep(time.Second)
num = 789
fmt.Println(<-c)

}

子协程,执行 1 返回 123,执行 2 返回 789
没看出来有什么差别啊?


更多关于Golang Go语言协程执行问题请教的实战教程也可以访问 https://www.itying.com/category-94-b0.html

17 回复

有区别,
1 进行了运算,取运算后的结果,你加了 1 秒的延时,很明显当时的 num 的值为 123,所以输出是 123,如果去掉延时,则为 789
2 一直指向 num 的地址,从通道里读的时候读的是 789

更多关于Golang Go语言协程执行问题请教的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


time.Sleep 不能保证 goroutine 一定比 main 先执行,如果想要让 Goroutine 比 main 先执行的话,可以通过 waitGroup 来同步:

https://gist.github.com/bwangelme/5e71895d40130521b71828cef72adc1f

c <- *p + x // 1. 返回 123 等于 tmp:= *p + x ; c<- tmp ,因为 chan 的 size 是 0 所以要等到主进程到 fmt.Println(<-c)时才会运行 c<- tmp

”By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value“
正解

正解, 1 2 楼答的不是这个问题


按照这个说法的话,应该两个值都会返回 123 才对啊?
为啥第 1 种返回 123,第 2 种返回 789

1 运算了,2 为啥不运算呢?

#7 2,向通道发送的 num 的地址,此时通道是阻塞的,过一秒后 num 因为赋值 789 地址发生改变,然后从通道里读取 num 值是 789

good ask, 我觉得我之前没有抓住对的点,我对 unbuffered channel 的解释其实并没有回答这个问题,这里的重点其实是
1, *p+x 会 create 一个新的 int ( 123 )存放在另一个地址,这个地址存放的值没有改变过
2,*p 会一直等于 num 的地址所存储的值,这个值在代码中被更改为 789
所以才会造成两个 case 结果不同


感谢

ch <- expression

expression 计算的时机是问题的关键。
具体的细节,希望大牛们给详细说说

这个问题和抛硬币一样。
c <-*p + x,既可能返回 123,也可能返回 789 。这和 thread 切换,先跑了哪个指令有关系(先跑了 num=789 还是后跑了),你可以把 sleep 时间修改小,证明这个现象。
go<br>package main<br><br>import (<br> "fmt"<br> "time"<br>)<br><br>var x = 0 <br><br>func call() {<br> var num = 123 <br> var p = &amp;num<br> c := make(chan int)<br> go func() {<br> c &lt;- *p + x // 1. 返回 123<br> // c &lt;- *p // 2. 返回 789<br> }() <br> time.Sleep(time.Second / 1000)<br> num = 789 <br> readValue := &lt;-c <br> if readValue == 789 {<br> fmt.Printf("hello 789\n")<br> } <br> //fmt.Println(readValue)<br>}<br><br>func main() {<br> for i := 0; i &lt; 1000000; i++ {<br> call()<br> } <br>}<br><br>

在 6c6t 的 cpu 上跑了一下,出现了一次 789 。


能详细解释下 1 这种情况既可能返回 123,也可能返回 789 吗?

主协程 sleep 1s,基本本可能输出 789 了。
如果主协程 sleep 时间很短,那么 num = 789 可能会比*p + x 先执行

这两天有点事就没有回答两位的问题。刚刚做了一些试验,发现了更有意思的地方。
1.如果写 chan 的地方是算术表达式,go 会提前进行预处理(对 c<-*p+0),可能已经换成 c<-123,所有大概率返回 123
2.如果是值,就换按正常逻辑往下走。time.Sleep 之后,两个 go 程开始了竞争,num=789 的执行速度,比唤醒生产者 chan+写数据快,所有大概率返回 789 。

当然上面从数据中总结的规律特别依赖 go 的版本(go 1.13.1),大家也不要太在意。也许哪天人家(Go Core Team)就改了。就当乐一乐,原来 go 还有一些小动作。
go<br>package main<br><br>import (<br> "fmt"<br> "time"<br>)<br><br>var x = 0<br><br>func call() {<br> var num = 123<br> var p = &amp;num<br> c := make(chan int)<br> go func() {<br> //c &lt;- *p + 0 //大概率返回 123<br> c &lt;- *p // 大概率返回 789<br> }()<br> time.Sleep(time.Second / 1000)<br> num = 789<br> readValue := &lt;-c<br> if readValue == 123 {<br> fmt.Printf("hello %d\n", readValue)<br> }<br>}<br><br>func main() {<br> for i := 0; i &lt; 1000000; i++ {<br> call()<br> }<br>}<br><br>

俺老孙直言,V2EX 这个防 spam 系统真的是人工智障

[TIP - 如何在回复中贴代码]( /t/663565 )

针对您提出的Golang协程执行问题,以下是一些专业的解答和建议:

  1. 协程无法正常运行的原因

    • 未正确使用sync.Mutexsync.RWMutex,可能导致死锁或资源竞争。
    • 协程阻塞,如等待I/O操作,可能导致其他协程饥饿。
    • 竞态条件,多个协程同时访问共享资源时未加锁或通信。
  2. 协程的正确使用

    • 使用go语句启动协程,确保函数在新协程中执行。
    • 使用channel进行协程间通信,确保数据一致性和安全性。
    • 使用sync.WaitGroup等同步工具,等待一组协程完成后再执行后续操作。
  3. 协程调度和管理

    • 避免协程泄漏,使用context包管理协程生命周期。
    • 优化协程创建和执行策略,减少不必要的协程创建,提高性能。
    • 监控和调整系统资源分配,确保协程公平获取执行机会。
  4. 建议

    • 在编写Go代码时,注意程序的健壮性,对协程的错误进行适当处理。
    • 对于涉及系统调用的操作,考虑设置超时时间或使用非阻塞I/O。

希望以上解答和建议能帮助您解决Golang协程执行问题。如有更多疑问,欢迎继续提问。

回到顶部