golang线程同步和并发内存模型思考

假设在一台4核CPU, 64位系统机子上运行以下程序输出什么?

func main() {
	runtime.GOMAXPROCS(0) 
	wg := sync.WaitGroup{}

	goRoutineCount := runtime.NumCPU()
	wg.Add(goRoutineCount)

	var loopNum uint64 = 100000000
	var x uint64 = 0
	for i := 0; i < goRoutineCount; i++ {
		go func() {
			for n := uint64(0); n < loopNum; n++ {
				x += 1
			}
			wg.Done()
		}()
	}
	wg.Wait()

	expectedNumber := uint64(goRoutineCount) * uint64(loopNum)
	if x != expectedNumber {
		fmt.Printf("错误\n")
	}else {
		fmt.Printf("正确\n")
	}
}

请思考…
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

错误

请思考原因…
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

原因: 线程同步问题, go普通类型的操作不是原子性的 , 请思考如何解决?

,
,
,
,
,
,
,
,
,
,
,
,
,

解决方案:

  • 1.使用同步原语,如sync.Mutex
  • 2.使用 channel进行同步
  • 3.使用原子操作 , 如sync/atomic
  • 4.其他同步操作

请自行实现方案1, 2, 3, 并运行…
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

使用原子操作:


func main() {
	runtime.GOMAXPROCS(0) 
	wg := sync.WaitGroup{}

	goRoutineCount := runtime.NumCPU()
	wg.Add(goRoutineCount)

	var loopNum uint64 = 100000
	var x uint64 = 0
	for i := 0; i < goRoutineCount; i++ {
		go func() {
			for n := uint64(0); n < loopNum; n++ {
				atomic.AddUint64(&x, 1)
			}
			wg.Done()
		}()
	}

	wg.Wait()

	expectedNumber := uint64(goRoutineCount) * uint64(loopNum)
	if x != expectedNumber {
		fmt.Printf("错误\n")
	} else {
		fmt.Printf("正确\n")
	}
}

使用mutex:


type MyCounter struct {
	sync.Mutex
	x uint64
}

func NewMyCounter(n uint64) *MyCounter {
	return &MyCounter{
		x: n,
	}
}

func (cnt *MyCounter) Add(n uint64) {
	cnt.Lock()
	defer cnt.Unlock()
	cnt.x += 1
}

func (cnt *MyCounter) Get() uint64 {
	return cnt.x
}

func main() {
	runtime.GOMAXPROCS(0) // CPU数
	wg := sync.WaitGroup{}

	goRoutineCount := runtime.NumCPU()
	wg.Add(goRoutineCount)

	var loopNum uint64 = 100000
	// var x uint64 = 0

	x := NewMyCounter(0)
	for i := 0; i < goRoutineCount; i++ {
		go func() {
			for n := uint64(0); n < loopNum; n++ {
				x.Add(1)
			}
			wg.Done()
		}()
	}

	wg.Wait()

	expectedNumber := uint64(goRoutineCount) * uint64(loopNum)
	if x.Get() != expectedNumber {
		fmt.Printf("错误\n")
	} else {
		fmt.Printf("正确\n")
	}
}

使用channel:


func main() {
	runtime.GOMAXPROCS(0) // CPU数
	wg := sync.WaitGroup{}

	goRoutineCount := runtime.NumCPU()
	wg.Add(goRoutineCount)

	var loopNum uint64 = 100000
	var x uint64 = 0

	// 思考: 如果改称 make(chan bool) 或 make(chan bool, 0)是否可以? make(chan bool, 10000)呢?
	ch := make(chan bool, 1)
	ch <- true
	for i := 0; i < goRoutineCount; i++ {
		go func() {
			for n := uint64(0); n < loopNum; n++ {
				<-ch
				x += 1
				ch <- true
			}
			wg.Done()
		}()
	}

	wg.Wait()

	expectedNumber := uint64(goRoutineCount) * uint64(loopNum)
	if x != expectedNumber {
		fmt.Printf("错误\n")
	} else {
		fmt.Printf("正确\n")
	}
}

思考: 用以下方式可以吗? 为什么?


func main() {
	runtime.GOMAXPROCS(0) // CPU数
	wg := sync.WaitGroup{}

	goRoutineCount := runtime.NumCPU()
	wg.Add(goRoutineCount)

	var loopNum uint64 = 100000
	var x uint64 = 0

	var flag bool = false
	for i := 0; i < goRoutineCount; i++ {
		go func() {
			for n := uint64(0); n < loopNum; n++ {
				for !flag {
				} // 等待flag
				flag = false 
				x += 1
				flag = true
			}
			wg.Done()
		}()
	}
	flag = true

	wg.Wait()

	expectedNumber := uint64(goRoutineCount) * uint64(loopNum)
	if x != expectedNumber {
		fmt.Printf("错误\n")
	} else {
		fmt.Printf("正确\n")
	}
}

,
,
,
,
,
,
,
,
,
,
,
,
,
,
,

在Go语言中,同一个Goroutine线程内部,顺序一致性内存模型是得到保证的。但是不同的Goroutine之间,并不满足顺序一致性内存模型,需要通过明确定义的同步事件来作为同步的参考。如果两个事件不可排序,那么就说这两个事件是并发的。为了最大化并行,Go语言的编译器和处理器在不影响上述规定的前提下可能会对执行语句重新排序(CPU也会对一些指令进行乱序执行)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值