Multithreading Go指针多线程读写错误
正常应为恒定输出Multithreading Go指针多线程读写错误,multithreading,pointers,go,unsafe,Multithreading,Pointers,Go,Unsafe,正常应为恒定输出 test1 test2 ........ 但只有test1输出,程序挂起,没有响应 指针的赋值是最基本的操作,这应该是线程安全的,以满足周期要求 但这项测试一直未能成功 type Point struct { X int Y int } func main() { var p *Point = nil test := true go func() { for test { if tmp := p
test1
test2
........
但只有test1输出,程序挂起,没有响应
指针的赋值是最基本的操作,这应该是线程安全的,以满足周期要求
但这项测试一直未能成功
type Point struct {
X int
Y int
}
func main() {
var p *Point = nil
test := true
go func() {
for test {
if tmp := p; tmp == nil {
p = &Point{}
}
}
}()
go func() {
for test {
if tmp := p; tmp != nil {
p = nil
}
}
}()
n := 0
for test {
n++
fmt.Printf("testing%v....\r\n",n)
time.Sleep(1000 * time.Millisecond)
}
fmt.Printf("test fail")
}
代码:
如果将指针更改为int,这很好
func main() {
var p int = 0
test := true
go func() {
for test {
if tmp := p; tmp == 0 {
p = 1
}
}
}()
go func() {
for test {
if tmp := p; tmp != 0 {
p = 0
}
}
}()
n := 0
for test {
n++
fmt.Printf("testing%v....\r\n",n)
time.Sleep(1000 * time.Millisecond)
}
fmt.Printf("test fail")
}
问题不仅在于有比赛,而且当有比赛时,日程安排会有问题。下面的代码具有一些选项,这些选项将使该代码生成所需的输出(已注释)。所以你要么:
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
type Point struct {
X int
Y int
}
var lock sync.Mutex
func main() {
// runtime.GOMAXPROCS(4)
fmt.Println("Max # of parallel processes = ", MaxParallelism())
var p *Point = nil
test := true
go func() {
for test {
// lock.Lock()
if tmp := p; tmp == nil {
p = &Point{}
}
// lock.Unlock()
// time.Sleep(1 * time.Nanosecond)
}
}()
go func() {
for test {
// lock.Lock()
if tmp := p; tmp != nil {
p = nil
}
// lock.Unlock()
// time.Sleep(1 * time.Nanosecond)
}
}()
n := 0
for test {
n++
fmt.Printf("testing%v....\r\n", n)
time.Sleep(1000 * time.Millisecond)
}
fmt.Printf("test fail")
}
func MaxParallelism() int {
maxProcs := runtime.GOMAXPROCS(0)
numCPU := runtime.NumCPU()
if maxProcs < numCPU {
return maxProcs
}
return numCPU
}
尽管有4个虚拟处理器,但问题似乎在于主处理器在第一次睡眠后从未被调度。显然,当有比赛条件的时候,这是莫名其妙地(至少对我来说)抬起了它丑陋的头。另一个观察结果是,如果我们减少两个goroutine中的一个,并且继续修改main循环中的p变量,那么它就可以工作了。所有这些似乎都给人一种印象,即当存在竞争条件时,运行时处理的行为是不可预测的。您的代码是快速的,因此未定义。不能同时写入同一变量。无需争论。使用并发时,请始终使用种族检测器运行测试(
go test-race
),因为您可能会对同一指针进行并发分配,这很糟糕。这就是为什么golang有select
、频道和sync
包,其中包含有用的小曲,如sync.Mutex
。另外:var p*Point=nil
可以缩短为var p*Point
谢谢。我现在知道如何处理这个问题了。这种行为的后果目前尚不清楚。整个应用程序被挂起,没有响应,这不利于发现问题。代码中有2个忙循环。不管比赛情况如何,这都会造成问题。谢谢。你的理解很好,我现在知道如何处理这个问题了。我想说的是,如果现在存在竞争,这种行为的后果是不可预测的。但是,整个应用程序被挂起,没有响应,这不利于处理问题。time.Sleep只解决了出错的概率,无法解决问题。locker是一个可行的选项,这就是为什么我提到锁定是正确的方法(正如@Volker评论中所暗示的)。上面的代码还用于演示不使用正确的同步原语的危险。
go run main.go
Max # of parallel processes = 4
testing1....