Golang Goroutines – 使用原子函数修复竞态条件

huangapple go评论76阅读模式
英文:

Golang Goroutines - Fix Race Condition using Atomic Functions

问题

我是一个 Golang 的新手,我正在尝试理解 goroutines。下面是我从 https://www.golangprograms.com/goroutines.html 上找到的一段代码。

package main

import (
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
)

var (
	counter int32          // counter 是所有 goroutine 增加的变量。
	wg      sync.WaitGroup // wg 用于等待程序完成。
)

func main() {
	wg.Add(3) // 添加计数,每个 goroutine 一个。

	go increment("Python")
	go increment("Java")
	go increment("Golang")

	wg.Wait() // 等待 goroutine 完成。
	fmt.Println("Counter:", counter)

}

func increment(name string) {
	defer wg.Done() // 调度 Done 来告诉主程序我们已经完成。

	for range name {
		fmt.Println("name:", name)
		fmt.Println("Counter in range:", counter)
		atomic.AddInt32(&counter, 1)
		runtime.Gosched() // 让出线程并重新排队。
	}
}

输出:

name: Golang
Counter in range: 0
name: Java
Counter in range: 1
name: Golang
Counter in range: 2
name: Golang
Counter in range: 3
name: Java
Counter in range: 4
name: Golang
Counter in range: 5
name: Python
Counter in range: 6
name: Java
Counter in range: 7
name: Golang
Counter in range: 8
name: Java
Counter in range: 9
name: Golang
Counter in range: 10
name: Python
Counter in range: 11
name: Python
Counter in range: 12
name: Python
Counter in range: 13
name: Python
Counter in range: 14
name: Python
Counter in range: 15
Counter: 16

我无法理解为什么输出是 16。即使我们只添加了 3 个 goroutine,它不应该是 3 吗?

有人可以解释一下吗?

谢谢。

英文:

I am a newbie in Golang and I am trying to understand the goroutines.
Here is the piece of code which I got from https://www.golangprograms.com/goroutines.html.

package main

import (
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
)

var (
	counter int32          // counter is a variable incremented by all goroutines.
	wg      sync.WaitGroup // wg is used to wait for the program to finish.
)

func main() {
	wg.Add(3) // Add a count of two, one for each goroutine.

	go increment("Python")
	go increment("Java")
	go increment("Golang")

	wg.Wait() // Wait for the goroutines to finish.
	fmt.Println("Counter:", counter)

}

func increment(name string) {
	defer wg.Done() // Schedule the call to Done to tell main we are done.

	for range name {
		fmt.Println("name:", name)
		fmt.Println("Counter in range:", counter)
		atomic.AddInt32(&counter, 1)
		runtime.Gosched() // Yield the thread and be placed back in queue.
	}
}

Output:

name: Golang
Counter in range: 0
name: Java
Counter in range: 1
name: Golang
Counter in range: 2
name: Golang
Counter in range: 3
name: Java
Counter in range: 4
name: Golang
Counter in range: 5
name: Python
Counter in range: 6
name: Java
Counter in range: 7
name: Golang
Counter in range: 8
name: Java
Counter in range: 9
name: Golang
Counter in range: 10
name: Python
Counter in range: 11
name: Python
Counter in range: 12
name: Python
Counter in range: 13
name: Python
Counter in range: 14
name: Python
Counter in range: 15
Counter: 16

I am unable to understand that why the output is 16. Even we added only 3 goroutines. Shouldn't it be 3?

Can anyone please explain me?

Thanks.

答案1

得分: 3

我无法理解为什么输出是16。即使我们只添加了3个goroutine,它也应该是3吗?

为什么应该是3呢?它应该是atomic.AddInt32(&counter, 1)被调用的次数。

那是多少次呢?你启动了3个goroutine,每个都有一个循环。递增操作是在循环内部完成的。

循环如下:

for range name {}

for range循环遍历字符串的字符。在你的例子中,字符串是GolangJavaPython。所以循环体将根据这些字符串的字符数执行相应的次数:Golang有6个字符,Java有4个字符,Python有6个字符,总共是16次。

英文:

> I am unable to understand that why the output is 16. Even we added only 3 goroutines. Shouldn't it be 3?

Why should it be 3? It should be as many times atomic.AddInt32(&counter, 1) is called.

How many times is that? You launch 3 goroutines, and each has a loop. Incrementing is done inside the loops.

The loops:

for range name {}

The for range over a string iterates over the runes of the string. The names in your case are Golang, Java and Python. So the loop bodies will be executed as many times as many runes these strings have: 6 for Golang, 4 for Java and 6 for Python, that adds up to 16.

答案2

得分: 0

你正在对给定的输入字符串进行范围遍历,trange将其视为数组并对其进行迭代。看看这个例子:

package main

import (
	"fmt"
)

func main() {
	increment("test")
	increment("test_dev")
	increment("test_deploy")
	
}

func increment(name string) {
	for i, value := range name {
		fmt.Printf("name: %s index: %d, char: %c\n", name, i, value)

	}
}

这段代码会输出字符串中每个字符的索引和值。

英文:

You are range over the given input string, trange treat it as an array and iterates over it.
See this example

package main

import (
	"fmt"
)

func main() {
	increment("test")
	increment("test_dev")
	increment("test_deploy")
	
}

func increment(name string) {
	for i, value := range name {
		fmt.Printf("name: %s index: %d, char: %c\n", name, i, value)

	}
}

huangapple
  • 本文由 发表于 2021年7月14日 15:11:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/68373579.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定