Go调度器和CGO:请解释一下它们之间的行为差异。

huangapple go评论78阅读模式
英文:

Go scheduler and CGO: Please explain this difference of behavior?

问题

我想知道这个代码的实现原因:

package main

func main() {
    c := make(chan struct{})

    go func() {
        print("a")
        for {
        }
    }()

    go func() {
        print("b")
        for {
        }
    }()

    go func() {
        print("c")
        c <- struct{}{}
        for {
        }
    }()

    <-c
}

GOMAXPROCS=2 go run sample1.go
ab  <--- 阻塞
package main

// static void loop() { for(;;); }
import "C"

func main() {
    c := make(chan struct{})

    go func() {
        print("a")
        C.loop()
        print("x")
    }()

    go func() {
        print("b")
        C.loop()
        print("y")
    }()

    go func() {
        print("c")
        c <- struct{}{}
        C.loop()
        print("z")
    }()

    <-c
}

GOMAXPROCS=2 go run sample2.go
abc  <--- 优雅地结束

更具体地说我想知道在 go routine 调度的上下文中C 的紧密循环与 Go 的循环有何不同尽管在 Go 程序结束时 C 的紧密循环应该被突然终止但我想知道是否可以安全地依赖这种行为来启动 C 任务而不阻塞 Go 程序

<details>
<summary>英文:</summary>

I&#39;d like to know the implementation reason for this:

    package main

    func main() {
    	c := make(chan struct{})
    
    	go func() {
    		print(&quot;a&quot;)
    		for {
    		}
    	}()
    
    	go func() {
    		print(&quot;b&quot;)
    		for {
    		}
    	}()
    
    	go func() {
    		print(&quot;c&quot;)
    		c &lt;- struct{}{}
    		for {
    		}
    	}()
    
    	&lt;-c
    }

    ❯❯❯ GOMAXPROCS=2 go run sample1.go                                    
    ab  &lt;--- blocks.

---

    package main
    
    // static void loop() { for(;;); }
    import &quot;C&quot;
    
    func main() {
    	c := make(chan struct{})
    
    	go func() {
    		print(&quot;a&quot;)
    		C.loop()
    		print(&quot;x&quot;)
    	}()
    
    	go func() {
    		print(&quot;b&quot;)
    		C.loop()
    		print(&quot;y&quot;)
    	}()
    
    	go func() {
    		print(&quot;c&quot;)
    		c &lt;- struct{}{}
    		C.loop()
    		print(&quot;z&quot;)
    	}()
    
    	&lt;-c
    }

    ❯❯❯ GOMAXPROCS=2 go run sample2.go                                    
    abc  &lt;--- ends gracefully.

More specifically I mean how the C tight loop differs from the Go one in the context of go routine scheduling. And even though the C tight loops should be being abruptly terminated at the end of the Go program, I&#39;d like to know whether it&#39;s safe to rely on this behavior to start C tasks without blocking the Go program.

</details>


# 答案1
**得分**: 4

运行时无法抢占真正的忙循环一个没有调度点的CPU密集型循环必须在自己的线程中运行以便其他goroutine能够运行函数调用和通道的发送或接收操作都会让出执行权网络IO是异步调度的文件IO有自己的线程

通常设置`GOMAXPROCS` > 1就足够了但由于你有3个这样的循环而只有2个线程调度器仍然被阻塞如果你有一个有效的CPU密集型循环使得调度goroutine变得困难你可以定期调用`runtime.GoSched()`来让出执行权给调度器在现实世界中这通常只在编程错误的情况下才会发生即出现了一个空循环

所有的cgo调用都在go运行时之外的它们自己的线程中进行所以这些循环对主循环没有影响除了浪费CPU

<details>
<summary>英文:</summary>

The runtime can&#39;t preempt a true busy-loop. A CPU intensive loop with no scheduling points has to be in it&#39;s own thread for other goroutines to be able to run. Function calls and channel send or receive operations all yield. Network IO is scheduled asynchronously, and file IO gets its own thread. 

Generally setting `GOMAXPROCS` &gt; 1 is sufficient, but since you have 3 of these loops, and only 2 threads, the scheduler is still blocked. If you have a valid CPU intensive loop that is making it difficult to schedule goroutines, you can call `runtime.GoSched()` periodically to yield to the scheduler. In the real world, the only place where this generally maters is a programming error where you have an empty loop.

All cgo calls happen in their own threads outside of the go runtime, so those loops have no effect on the main loop, other than wasting CPU.  

</details>



huangapple
  • 本文由 发表于 2015年3月3日 23:36:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/28835758.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定