如何通过通道高效地将在goroutine中分配的结构体传递回主例程?

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

How do I efficiently pass a struct allocated in a goroutine back to the main routine via a channel?

问题

基本上,我有一个主程序,它会生成多个goroutine来处理数据。每当一个goroutine处理数据时,它会发送回一个大小不同的结构体(其中包含每次从goroutine内部分配的切片和/或数组)。

数据并不是很大(比如几兆字节),但一般来说,传输数据的指针比传输全部数据的副本更高效(并且更安全)吗?如果数据结构是静态的,并且我传输一个指向它的指针,那么在我仍在处理上一次调用的结果时,结构体可能会发生变化(如果它完全重新分配,那可能不是问题)。

英文:

At a basic level, I have a main routine that spawns multiple goroutines to process data. Every time a goroutine processes the data it sends back a struct of varying size (it contains slices and/or arrays allocated from within the goroutine each time).

The data isn't huge (say, a few megabytes) but in general is it more efficient (and is it safe) to transfer a pointer to the data versus a copy of it all? If the data structure is static and I transfer a pointer to it, there's a risk that the structure may change while I'm still processing the result of the previous invocation (if it's fully reallocated then perhaps that's not an issue).

答案1

得分: 4

发送指向值的指针是可以的,也是常见的做法。如果值很大,发送指向值的指针比发送值本身更高效。可以运行基准测试来确定何为“大”。

但需要注意的是,必须防止对值进行不安全的并发访问。常见的防止不安全并发访问的策略有:

  • 将值的所有权从发送者传递给接收者。发送者在发送后不再访问该值。接收者可以对该值进行任何操作。
  • 在发送后将该值视为只读。发送者和接收者都不会修改该值。
英文:

It's OK and common to send pointers to values. If the value is large, sending a pointer to the value will be more efficient than sending the value. Run a benchmark to find out how large is "large".

The caveat is that you must prevent unsafe concurrent access to the value. Common strategies for preventing unsafe concurrent access are:

  • Pass ownership of the value from the sender to the receiver. The sender does not access the value after sending it. The receiver can do whatever it wants with the value.
  • Treat the value as read only after sending. Neither the sender or receiver modifies the value after sending.

答案2

得分: 4

根据我的理解,你想要做的是:

func watchHowISoar() (ch chan *bigData) {
    ch = make(chan *bigData)
    go func() {
        for i := 0; i < 10; i++ {
            bd := &bigData{i}
            ch <- bd
            // 只要在发送后不在这个 goroutine 中修改 bd,就是安全的。
        }
        close(ch)
    }()
    return
}
func main() {
    for iamaleafOnTheWind := range watchHowISoar() {
        fmt.Printf("%p\n", iamaleafOnTheWind)
    }
}

只要在发送数据后不修改发送方的数据,这段代码是完全安全的。

如果你有疑问,可以尝试使用 go run -race main.go 运行它,虽然竞争检测器不是完美的,但通常可以检测到这类问题。

英文:

From my understanding you're trying to do something like:

func watchHowISoar() (ch chan *bigData) {
	ch = make(chan *bigData)
	go func() {
		for i := 0; i &lt; 10; i++ {
			bd := &amp;bigData{i}
			ch &lt;- bd
			// as long as you don&#39;t modify bd inside this goroutine after sending it, you&#39;re safe.
		}
		close(ch)
	}()
	return
}
func main() {
	for iamaleafOnTheWind := range watchHowISoar() {
		fmt.Printf(&quot;%p\n&quot;, iamaleafOnTheWind)
	}
}

And it is perfectly safe as long as you don't modify the sent data from the sender after you send it.

If you have doubts, try to run it with go run -race main.go, while the race detector isn't perfect, it will usually detect things like that.

huangapple
  • 本文由 发表于 2014年9月26日 10:37:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/26051204.html
匿名

发表评论

匿名网友

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

确定