在Go语言中,同时读取/访问数组是否是线程安全的?

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

Is it thread safe to concurrently read/access an array in go?

问题

如果我有一个包含数组的结构体,并且我想要像这样做:

type Paxos struct {
	peers []string
}

for _, peer := range px.peers {
   // 做一些操作
}

我的例程/线程永远不会修改peers数组,只会从中读取。Peers是一个服务器地址的数组,服务器可能会失败,但这不会影响peers数组(后续的rpc调用只会失败)。

英文:

Like if I have a struct with an array and I want to do something like this

type Paxos struct {
	peers      []string
}

for _, peer := range px.peers {
   \\do stuff
}

My routines/threads will never modify the peers array, just read from it. Peers is an array of server addresses, and servers may fail but that wouldn't affect the peers array (later rpc calls would just fail)

答案1

得分: 2

如果没有涉及写操作,无论数据结构如何,同时进行的读操作总是安全的。然而,一旦涉及到对变量的单个并发不安全的写操作,就需要对变量的并发访问(包括写操作和读操作)进行串行化。

此外,只要不超过一个goroutine对给定元素进行写操作,就可以安全地对切片或数组的元素进行写操作。

例如,如果你在启用了竞争检测器的情况下运行以下程序,很可能会报告竞争条件,因为多个goroutine在没有预防措施的情况下同时修改变量results

package main

import (
	"fmt"
	"sync"
)

func main() {
	const n = 8
	var results []int
	var wg sync.WaitGroup
	wg.Add(n)
	for i := 0; i < n; i++ {
		i := i
		go func() {
			defer wg.Done()
			results = append(results, square(i))
		}()
	}
	wg.Wait()
	fmt.Println(results)

}

func square(i int) int {
	return i * i
}

然而,以下程序不包含这种同步错误,因为切片的每个元素都由单个goroutine修改:

package main

import (
	"fmt"
	"sync"
)

func main() {
	const n = 8
	results := make([]int, n)
	var wg sync.WaitGroup
	wg.Add(n)
	for i := 0; i < n; i++ {
		i := i
		go func() {
			defer wg.Done()
			results[i] = square(i)
		}()
	}
	wg.Wait()
	fmt.Println(results)

}

func square(i int) int {
	return i * i
}
英文:

If no writes are involved, concurrent reads are always safe, regardless of the data structure. However, as soon as even a single concurrency-unsafe write to a variable is involved, you need to serialise concurrent access (both writes and reads) to the variable.


Moreover, you can safely write to elements of a slice or an array under the condition that no more than one goroutine write to a given element.

For instance, if you run the following programme with the race detector on, it's likely to report a race condition, because multiple goroutines concurrently modify variable results without precautions:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

func main() {
	const n = 8
	var results []int
	var wg sync.WaitGroup
	wg.Add(n)
	for i := 0; i &lt; n; i++ {
		i := i
		go func() {
			defer wg.Done()
			results = append(results, square(i))
		}()
	}
	wg.Wait()
	fmt.Println(results)

}

func square(i int) int {
	return i * i
}

However, the following programme contains no such no synchronization bug, because each element of the slice is modified by a single goroutine:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

func main() {
	const n = 8
	results := make([]int, n)
	var wg sync.WaitGroup
	wg.Add(n)
	for i := 0; i &lt; n; i++ {
		i := i
		go func() {
			defer wg.Done()
			results[i] = square(i)
		}()
	}
	wg.Wait()
	fmt.Println(results)

}

func square(i int) int {
	return i * i
}

答案2

得分: 1

是的,在Go语言和几乎所有其他语言中,读操作是线程安全的。你只是在内存中查找一个地址并查看其中的内容。如果没有任何尝试修改该内存的操作,那么你可以同时进行多个并发读取操作。

英文:

Yes, reads are thread-safe in Go and virtually all other languages. You're just looking up an address in memory and seeing what is there. If nothing is attempting to modify that memory, then you can have as many concurrent reads as you'd like.

huangapple
  • 本文由 发表于 2022年3月10日 00:50:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/71413041.html
匿名

发表评论

匿名网友

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

确定