GOMAXPROCS为1时是否需要锁定

GOMAXPROCS为1时是否需要锁定,go,Go,GOMAXPROCS变量限制操作系统的数量 可以同时执行用户级Go代码的线程 因此,如果GOMAXPROCS为1,则无论我有多少个goroutine,都可以安全地从不同的goroutine访问变量(如map),而无需任何锁定。正确吗?是的,即使在一个处理器上运行程序,仍然需要锁。并发和并行是不同的东西,你会发现一个很好的解释 这里只举一个小例子: func main() { runtime.GOMAXPROCS(1) t := &test{} go func(

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


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

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

这里只举一个小例子:

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)

}
func main(){
runtime.GOMAXPROCS(1)
t:=&测试{}
go func(){
对于i:=0;i<100;i++{
//使用t.Num之前的一些计算
时间。睡眠(300*时间。微秒)
num:=t.num
//使用num的一些计算
时间。睡眠(300*时间。微秒)
t、 Num=Num+1
}
}()
go func(){
对于i:=0;i<100;i++{
num:=t.num
//使用num的一些计算
时间。睡眠(300*时间。微秒)
t、 Num=Num+1
}
}()
time.Sleep(1*time.Second)//等待goroutines完成
格式打印项次(t.Num)
}
睡眠时间是用来表示需要一些时间的计算。我想让示例保持可运行和简单,这就是我使用它的原因

即使在单个处理器上运行,输出也不是我们想要的200。因此,是的,当同时访问变量时,锁是必要的,否则您将遇到问题。

简单的回答是,“不”,这是不安全的。很长的答案实在太长,无法在这里进行足够详细的解释,但我将给出一个简短的摘要和一些文章链接,这些链接将有助于您将这些内容组合起来

让我们首先区分“并发”和“并行”。考虑两个函数。并行运行它们可以同时在不同的处理器上执行。同时运行或同时运行,或两者都不能执行,但两者都可以执行。如果它们是并发的,但不是并行的,那么它们是切换的,并且没有通道或锁,我们无法保证序列中哪个先到达哪里

思考“并发但不平行”可能是奇怪的,但认为相反是相当不显著的,平行的,但不同时的;我的文本编辑器、终端和浏览器都是并行运行的,但绝对不是并行运行的

因此,如果两个(或20000个)函数可以访问同一个内存,比如说一个写一个读,并且它们同时运行,那么可能是先写,也可能是先读。除非我们负责调度/排序,否则无法保证,因此锁定和通道

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

线程[通常]由操作系统调度。查看或查看您最喜爱的人类知识库。Goroutines由Go安排

下一步考虑:

即使只有一个逻辑处理器和操作系统线程,也可以安排数十万个goroutine以惊人的效率和性能并发运行

这是:

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

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


最后,当你刚开始的时候,有效的围棋可能会让人望而生畏,但这是一本必读的书。是对Go中并发性的解释。

在运行时更改状态。假定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
)
func main(){
runtime.GOMAXPROCS(1)
开始:=make(chan结构{})
wg:=&sync.WaitGroup{}
N:=2//10、1000、10000,…即使有两个go例程也会失败
对于i:=0;i不。Go例程和线程是完全不同的东西,并发访问意味着您无法保证执行顺序;仍然需要锁和/或通道。