在Go语言中,通过将程序设置为单核并使用阻塞操作,可以实现并发。

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

Concurrency is achieved in Go when setting to single core with blocking operations

问题

我正在测试Go语言中的阻塞操作以及它如何剥夺其他goroutine的处理器共享。所以我进行了以下测试:

package main
    
import (
    "fmt"
    "runtime"
    "time"
)
    
func test2() {
    for i := 1; ; i++ {
        fmt.Println(i, time.Now())
    }
}
    
func test() {
    a := 100
    for i := 1; i < 1000; i++ {
        a = i*100/i + a
        fmt.Println("value: ", a)
    }
}
    
func main() {
    runtime.GOMAXPROCS(1)
    go test2()
    for {
        test()
    }
}

正如你在示例中看到的,main()函数的第一行我设置Go语言只使用一个核心,并且运行一个无限循环的任务,以阻塞其他进程。然而,我发现了一些我没有预料到的结果,我发现testtest2两个函数都在运行,每个函数都有它自己的时间片(每个进程的时间片比将GOMAXPROCS设置为更高值时更长)。这是输出结果:

https://gist.github.com/anonymous/b3634be74d30fd36f552

我该如何解释这个现象?

P.S. 我使用的是Go版本1.5.3

更新

我做了一个小改动,将GOMAXPROCS设置为2,从test函数中移除了fmt.Println("value: ", a),现在程序运行一段时间后,test2函数运行,然后test函数接管,没有其他东西运行!

英文:

I was testing how a blocking operation works on Go, and how it deprives other go-routines from having processor share, so I did that test:

package main
    
import (
    &quot;fmt&quot;
    &quot;runtime&quot;
    &quot;time&quot;
)
    
func test2() {
    for i := 1; ; i++ {
        fmt.Println(i, time.Now())
    }
}
    
func test() {
    a := 100
    for i := 1; i &lt; 1000; i++ {
        a = i*100/i + a
        fmt.Println(&quot;value: &quot; , a )
    }
}
    
func main() {
    runtime.GOMAXPROCS(1)
    go test2()
    for {
        test()
    }
}

As you can see in the example, first line of main() I'm setting Go to use a single core and a never ending task runs on, so that it blocks any other process, though I see results I didn't expect, I found out that both test and test2 are running, each having it's time share (a larger time share per process that's longer than if I set the GOMAXPROCS to higher values). This is the output:

https://gist.github.com/anonymous/b3634be74d30fd36f552

How can I explain this?

P.S. I'm using Go version 1.5.3

Update

I made a little change by setting GOMAXPROCS to 2, removed fmt.Println(&quot;value: &quot; , a ) from test function, now, the program runs test2 for some time and, the test function takes over and nothing else run!

答案1

得分: 4

一个重要的区别是,GOMAXPROCS=1并不限制运行时只使用一个核心,它限制的是同时运行用户代码的操作系统线程数量为1。所有(由gc派生的)Go程序都是多线程的。

在大多数情况下,忙碌循环会干扰调度器和/或垃圾回收,有时即使GOMAXPROCS > 1也是如此。然而,在你的例子中,对test()fmt.Println函数的调用是调度点,这些调度点允许运行时调度器执行其他goroutine。


正如你指出的,如果从test函数中移除对fmt.Println的调用,test2()的进度最终会停止(显然仅调用test()并不足以让调度器继续执行。也许是因为它太快了,被内部的for循环内联,被阻塞等等)。

在这种情况下,忙碌循环对test()的调用阻止了垃圾回收的停止-全局阶段,并阻塞了调度器。你可以通过使用GOGC=off来验证这一点。

调度、CPU绑定任务、垃圾回收等的详细信息可能会因版本而异,但由于goroutine是协作调度的,忙碌循环总是一个错误。如果你确实需要一个长时间运行的、CPU绑定的循环,你可以通过偶尔插入对runtime.Gosched()的调用来与调度器合作,让出给其他goroutine执行的机会。

英文:

An important distinction is that GOMAXPROCS=1 doesn't limit the runtime to 1 core, it limits the number of OS threads actively running user code to 1. All (gc derived) Go programs are multithreaded.

In most cases a busy loop will interfere with the scheduler and/or garbage collection, sometimes even if GOMAXPROCS &gt; 1. In your example though, the calls to the test() and fmt.Println functions are scheduling points, and those allow the runtime scheduler to execute other goroutines.


As you pointed out, if the call to fmt.Println is removed from test, the progress on test2() eventually stops (apparently the call to test() alone isn't sufficient to let the scheduler proceed. Maybe it's to fast, inlined, blocked by the internal for loop, so on).

In this case it is because the busy-loop calls to test() are preventing the GC from doing it's stop-the-world phase of garbage collection, and blocking the scheduler. You can verify this by running the program with GOGC=off.

The details of scheduling, cpu-bound tasks, garbage collection, etc. can change from release to release, but since goroutines are cooperatively scheduled, a busy-loop is always a mistake. If you have an actual need for a long running, cpu-bound loop, you can cooperate with the scheduler by ocasionally inserting a call to runtime.Gosched() to yield to other goroutines.

huangapple
  • 本文由 发表于 2016年2月20日 00:11:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/35509753.html
匿名

发表评论

匿名网友

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

确定