同时写入和读取数组的最惯用方式是什么?

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

Most idiomatic way of writing and reading an array at the same time

问题

我有一个数组,每秒钟会被一个goroutine修改几次。在另一个函数中,我每隔几秒钟从数组中读取一个随机值。在Go语言中,最符合Go风格的方法是什么?

例如,在Java中,我会先锁定数组,然后再从中读取。我不确定是否可以使用通道来解决这个问题,因为我不想等到另一个函数读取完毕才能继续更新数组。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

const (
	SliceLength = 32
)

var mySlice []int

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func writeSlice() {
	for index, _ := range mySlice {
		mySlice[index] = rand.Intn(100)
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	mySlice = make([]int, SliceLength)

	// 第一次用于填充数组。
	writeSlice()

	// 写入数组。
	go func() {
		for {
			time.Sleep(time.Duration(randInt(10, 50)) * time.Millisecond)
			writeSlice()
		}
	}()

	// 从数组中读取。
	for {
		time.Sleep(time.Duration(randInt(1, 5)) * time.Second)
		fmt.Println(mySlice[randInt(0, SliceLength)])
	}
}
英文:

I have one array that gets modified a few times per second with one goroutine. In another function, I read a random value from the array once every few seconds. What would be the most Go-like way of approaching this problem?

In Java, for example, I would lock the array first and then read from it. I'm not sure if I could use channels for this because I don't want to wait until my other function reads before I can continue updating the array.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

const (
	SliceLength = 32
)

var mySlice []int

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func writeSlice() {
	for index, _ := range mySlice {
		mySlice[index] = rand.Intn(100)
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	mySlice = make([]int, SliceLength)

	// First time just to populate it.
	writeSlice()

    // Write to the slice.
	go func() {
		for {
			time.Sleep(time.Duration(randInt(10, 50)) * time.Millisecond)
			writeSlice()
		}
	}()

    // Read from slice.
	for {
		time.Sleep(time.Duration(randInt(1, 5)) * time.Second)
		fmt.Println(mySlice[randInt(0, SliceLength)])
	}
}

答案1

得分: 1

一个互斥锁 sync.RWMutex 可以在这里起作用。你只需要在两个 goroutine 之间共享你的切片,在每个切片操作之前/之后进行锁定和释放。

当你探索可能性时,你还应该考虑“共享无事务”原则的含义。你希望找到一种不使用互斥锁的解决方案,每个 goroutine 都只有私有数据 - 共享无事务。

假设你有一个第三个 goroutine。将其视为一个服务器。它保存切片(或任何其他需要的争用数据结构)。当第一个 goroutine 想要进行更改时,它会通过通道向服务器发送一条消息,告诉它“将 x 更改为 y”。当第二个 goroutine 想要读取数据时,它可能只需从服务器发送的通道中读取数据的副本。

在服务器内部,一个 select 语句将在传入的更新消息和传出的读取消息之间进行选择 - 请记住,保护可以在任一通道输入或通道输出上,所以这很容易做到。

...  声明并初始化切片
for {
    select {
    case instruction = <-update:
        ...  将指令应用于切片
    case output<- slice:
        ...  将切片发送给消费者
    }
}

请注意,服务器代码本身是单线程的,即使整体行为是并发的,也没有对切片的共享内存访问。这是 CSP 方法的一个很好的特点:在本地,永远不需要担心对数据的共享访问。 "共享无事务"就是字面意思。

此外,你可以完全推理出这段代码的可能状态,而不需要全局知识。这是一个很大的好处。

英文:

A mutex sync.RWMutex can work here. You simply share your slice between two goroutines, locking and releasing before/after each slice operation.

Whilst you're exploring possibilities, you should also consider what the "shared-nothing" principle would mean. You would want to find a solution that does not use a mutex and in which each goroutine has private data only - shared-nothing.

Suppose you had a third goroutine. Think of it as a server. It holds the slice (or any other contended data structure as required). When your first goroutine wants to make a change, it sends a message on a channel to the server, saying "change x to y". When your second goroutine wants to read the data, it might simply read a copy of the data off a channel coming from the server.

Inside the server, a select statement will choose between incoming update messages and outgoing read messages - bear in mind that the guards can be on either channel inputs or channel outputs, so this is easy to do.

...  declare and initialise the slice
for {
    select {
    case instruction = <-update:
        ...  apply the instruction to the slice
    case output<- slice:
        ...  sent the slice to the consumer
    }
}

Notice that the server code is itself single-threaded, i.e. there is no shared-memory access to the slice, even though the behaviour overall is concurrent. This is a nice feature of the CSP approach: locally, there is never any need to worry about shared access to data. "Shared-nothing" means what it says.

Furthermore, you can fully reason about the possible states of such a piece of code locally, without needing global knowledge. This is a BIG benefit.

答案2

得分: 0

听起来这是 sync.RWMutex 的工作。

>RWMutex 是一个读写互斥锁。该锁可以被任意数量的读取者或单个写入者持有。RWMutex 可以作为其他结构的一部分创建;RWMutex 的零值是一个未锁定的互斥锁。

英文:

Sounds like a job for sync.RWMutex.

>An RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. RWMutexes can be created as part of other structures; the zero value for a RWMutex is an unlocked mutex.

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

发表评论

匿名网友

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

确定