GOMAXPROCS为1时是否需要锁定

Mr.*_*oor 3 go

GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程的数量。

因此,如果GOMAXPROCS为1,则无论我有多少个goroutine,都可以安全地map从不同goroutine中访问变量(如),而无需进行任何锁定。正确?

T. *_*rie 5

是的,即使您在一个处理器上运行程序,仍然需要锁。并发和并行是不同的东西,你会在这里找到一个很好的解释。

这里只是一个小例子:

func main() {
    runtime.GOMAXPROCS(1)

    t := &test{}

    go func() {
        for i := 0; i < 100; i++ {
            // Some computation prior to using t.Num
            time.Sleep(300 * time.Microsecond)
            num := t.Num
            // Some computation using num
            time.Sleep(300 * time.Microsecond)
            t.Num = num + 1
        }
    }()

    go func() {
        for i := 0; i < 100; i++ {
            num := t.Num
            // Some computation using num
            time.Sleep(300 * time.Microsecond)
            t.Num = num + 1
        }
    }()

    time.Sleep(1 * time.Second) // Wait goroutines to finish

    fmt.Println(t.Num)

}
Run Code Online (Sandbox Code Playgroud)

睡眠时间代表一些需要一些时间的计算。我想保持示例的可运行性和简单性,这就是我使用它的原因。

运行它时,即使在单个处理器上,输出也不是我们想要的 200。所以是的,并发访问变量时需要锁,否则你会遇到问题。


Sno*_*man 5

简短的答案是,“不”是不安全的。长答案的时间太长了,无法在此处进行足够详细的解释,但是我将给出一个简短的摘要以及一些文章的链接,这些文章应该可以帮助您将各个部分组合在一起。

让我们先区分“并行”和“并行”。考虑两个功能。并行运行时,它们都可以在不同的处理器上同时执行。同时运行一种或两种,或者两者都不可以被执行,但两者都能够执行。如果它们是并发的而不是并行的,那么它们将在切换-并且没有通道或锁,我们不能保证顺序从哪儿先开始。

考虑“并发而不是并行”可能很奇怪,但认为相反的情况并不明显,可以并行但不能并发。我的文本编辑器,终端和浏览器都是并行运行的,但绝对不是并行的。

因此,如果两个(或20,000个)函数可以访问同一内存,比如说一次写入和一次读取,并且它们同时运行,那么也许首先发生写入操作,或者首先发生读取操作。除非我们对调度/排序负责,否则不做任何保证,因此不对锁和通道负责。

将GOMAXSPROCS设置为大于1可使并发程序并行运行,但事实并非如此,所有并发 goroutine可能在一个CPU线程上,也可能在多个CPU线程上。因此,将GOMAXPROCS设置为1不能保证并发进程在没有锁或通道来协调其执行的情况下是安全的。

线程通常是由操作系统调度的。请参阅Wikipedia或您最喜欢的人类知识库。Goroutine由Go安排。

接下来考虑一下:

即使使用单个逻辑处理器和操作系统线程,也可以安排成千上万个goroutine以惊人的效率和性能同时运行。

和这个:

将并发构建到应用程序中的问题是,goroutines最终将尝试同时可能访问相同的资源。对共享资源的读写操作必须始终是原子的。换句话说,读写必须一次执行一次例行程序,否则我们将在程序中创建竞争条件。

这篇文章中,这也解释了区别真的很好,引用您可能想看看一些其他材料(文章有些过时了,因为GMAXPROCS不再默认为1,但总的理论仍然是准确的)。

最后,当您刚起步时,Effective Go可能会令人生畏,但这是必读的。是Go中并发的解释。