GOMAXPROCS
变量限制了操作系统的数量 可以同时执行用户级Go代码的线程。
因此,如果GOMAXPROCS
为1,无论我有多少goroutine,都可以安全地从不同的goroutine访问变量(如map
)而无需任何锁定。正确的吗?
答案 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以惊人的效率和性能同时运行。
和此:
来自this article的在我们的应用程序中构建并发性的问题最终是我们的goroutine将尝试同时访问相同的资源。针对共享资源的读写操作必须始终是原子的。换句话说,读取和写入必须一次由一个goroutine发生,否则我们会在程序中创建竞争条件。
,它很好地解释了差异,并引用了一些你可能想要查找的其他材料(文章有些过时,因为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
)
其他答案更多细节 - 有利于研究并发编程的不同方面。