当GOMAXPROCS为1时需要锁定

时间:2016-05-23 12:26:21

标签: go

  

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

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

3 个答案:

答案 0 :(得分:4)

是的,即使您在一个处理器上运行程序,仍然需要锁定。并发和并行是不同的事情,你会找到一个非常好的解释here

这里只是一个小例子:

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)

}

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

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

答案 1 :(得分:4)

简短的回答是,&#34; no&#34;这不安全。很长的答案真的太长了,无法在这里详细解释,但我会给出简短的总结和一些文章的链接,这些文章可以帮助你把各个部分放在一起。

让我们区分&#34;并发&#34;和&#34;平行&#34;第一。考虑两个功能。并行运行它们可以在不同的处理器上同时执行。同时运行或同时运行,或者两者都不执行可能正在执行,但两者都能够执行。如果它们是并发的但不是并行的那么它们是切换的 - 并且没有通道或锁定,我们不能保证序列在哪个方面首先获得。

考虑&#34;并发但不平行&#34;可能很奇怪。但是考虑到相反的情况是相当不起眼的,平行的但不是并发的;我的文本编辑器,终端和浏览器都是并行运行的,但绝对不是并发的。

因此,如果两个(或20,000个)函数可以访问相同的内存,比如一个写入和一个读取,并且它们同时运行,那么可能首先发生写入,也许首先发生读取。除非我们负责调度/排序,因此无法保证,因此锁定和通道。

将GOMAXSPROCS设置为大于1可以使并发程序并行运行,但可能不会,所有并发 goroutine可能在一个CPU线程上,或者它们可能在多个上。因此,将GOMAXPROCS设置为1并不能保证并发进程是安全的,没有锁或通道来协调它们的执行。

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

接下来考虑一下:

  

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

和此:

  

在我们的应用程序中构建并发性的问题最终是我们的goroutine将尝试同时访问相同的资源。针对共享资源的读写操作必须始终是原子的。换句话说,读取和写入必须一次由一个goroutine发生,否则我们会在程序中创建竞争条件。

来自this article

,它很好地解释了差异,并引用了一些你可能想要查找的其他材料(文章有些过时,因为GMAXPROCS不再默认为1,但一般理论仍然准确)

最后,当你开始使用时,Effective Go可能会令人生畏,但必须阅读。 Here是对Go的并发性的解释。

答案 2 :(得分:0)

假设runtime.GOMAXPROCS(1),改变状态将失败;即使只是两个常规程序:

func main() {
    runtime.GOMAXPROCS(1)

    start := make(chan struct{})
    wg := &sync.WaitGroup{}

    N := 2 //10, 1000, 10000, ... fails with even 2 go-routines

    for i := 0; i < N; i++ {
        wg.Add(1)

        go func() {
            defer wg.Done()
            <-start

            //processing state
            initialState := globalState

            //give another goroutine a chance, by halting this one
            //and lend some processing cycles
            //(also simulating "concurrent" processing of initialState)
            runtime.Gosched()

            if globalState != initialState {
                panic(fmt.Sprintf("oops! %d != %d", initialState, globalState))
            }
            globalState = initialState + 1
        }()
    }

    close(start)
    wg.Wait()

    log.Println(`global state:`, globalState)
}

var (
    globalState int
)

其他答案更多细节 - 有利于研究并发编程的不同方面。