Golang中关于https://play.golang.org/p/FCydAiNZNWv的一些疑问

Golang中关于https://play.golang.org/p/FCydAiNZNWv的一些疑问 https://play.golang.org/p/FCydAiNZNWv

我不理解这个:time.Sleep(time.Second * 2) 我不理解这个:runtime.Gosched() // 告诉运行时将处理器让给其他 goroutine “counter” 是什么?

3 回复

cherilexvold1974:

time.Sleep(time.Second * 2)

你好,Cherolyn,根据这部分代码被注释掉,然后紧接着是 runtime.Gosched() 的情况,我的印象是,写这段代码的人试图强制一个 goroutine 进入休眠状态,以便另一个 goroutine 能够运行。counter 是一个全局变量,在那个 for 循环中创建的 goroutines 正在递增它。

不过,如果没有更多的上下文,我不太确定这段代码本意是要做什么或者演示什么。

time.Sleep(time.Second * 2)

更多关于Golang中关于https://play.golang.org/p/FCydAiNZNWv的一些疑问的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


time.Sleep(time.Second * 2) 的作用是让 goroutine 暂停 2 秒。不过它已经被注释掉了。它的副作用是会让其他 goroutine 得以运行。

runtime.Gosched() 告诉运行时暂停当前 goroutine,让其他 goroutine 运行。这是一种特殊的暂停,因为其他 goroutine 将会运行。它完成所需的时间远少于 2 秒。

counter 是一个会被递增的全局变量。

我猜这段代码是为了演示并发递增计数器的效果。在这段代码中,有一个互斥锁保护着递增操作。由于互斥锁的存在,只有一个 goroutine 可以执行 LockUnlock 之间的代码。最终的结果是,计数器的最终值将是 gs 的值。

如果没有互斥锁的 LockUnlock,计数器的值将会不同(小于 gs)。

这个代码演示了goroutine并发访问共享变量时的竞态条件问题。让我逐一解释:

package main

import (
	"fmt"
	"runtime"
	"time"
)

var counter int // counter是一个全局整型变量,被多个goroutine共享访问

func main() {
	runtime.GOMAXPROCS(2) // 设置使用2个CPU核心
	
	go incCounter(1) // 启动第一个goroutine
	go incCounter(2) // 启动第二个goroutine
	
	time.Sleep(time.Second * 2) // 主goroutine休眠2秒,等待其他goroutine执行
	fmt.Printf("Final Counter: %d\n", counter)
}

func incCounter(id int) {
	for count := 0; count < 2; count++ {
		value := counter // 读取counter当前值
		runtime.Gosched() // 主动让出CPU,让其他goroutine运行
		value++ // 增加本地副本的值
		counter = value // 写回全局变量
		fmt.Printf("Goroutine %d: Counter = %d\n", id, counter)
	}
}

关于time.Sleep(time.Second * 2) 这是为了让主goroutine等待其他goroutine完成。如果没有这个等待,主goroutine会立即结束,程序退出,其他goroutine可能来不及执行。

关于runtime.Gosched() 这个调用主动让出当前goroutine的CPU时间片,让调度器可以运行其他goroutine。在这个例子中,它故意制造竞态条件,让两个goroutine交替执行,更容易观察到数据竞争。

关于counter counter是一个全局共享变量,两个goroutine都会对它进行读取、修改、写入操作。由于没有同步机制,会出现以下竞态情况:

可能的执行顺序:

  1. Goroutine1读取counter=0
  2. Goroutine1让出CPU
  3. Goroutine2读取counter=0
  4. Goroutine2让出CPU
  5. Goroutine1写入counter=1
  6. Goroutine2写入counter=1(覆盖了Goroutine1的结果)

最终counter可能是2(理想情况),也可能是1(发生了数据竞争)。

要修复这个问题,应该使用同步原语如mutex:

var (
	counter int
	mutex   sync.Mutex
)

func incCounter(id int) {
	for count := 0; count < 2; count++ {
		mutex.Lock()
		value := counter
		value++
		counter = value
		mutex.Unlock()
		fmt.Printf("Goroutine %d: Counter = %d\n", id, counter)
	}
}
回到顶部