通过通道传递指针是否破坏了 CSP(通信顺序进程)设计?

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

golang does passing pointer through channel break the csp design?

问题

以下是您提供的代码的翻译:

type Data struct {
	data int
}

func printData(c chan *Data) {
	time.Sleep(time.Second * 1)
	data := <-c
	for {
		data.data = 100
	}
}

func main() {
	fmt.Println("Main started...")
	a := Data{data: 1}
	b := &a

	// 创建通道
	c := make(chan *Data, 10)
	go printData(c)
	fmt.Println(fmt.Printf("Value of b before putting into channel %v", *b))
	c <- b
	for {
		b.data = 20
	}
}

我使用 go build -race 命令构建二进制文件,如预期的那样,发生了数据竞争。

我的疑问是,我认为传递指针是不正确的,因为在使用带有多个 goroutine 的通道时,通道本身已经有一个锁,但是将指针传递给通道会导致 goroutine 需要添加另一个锁来保护数据竞争。这很奇怪。

所以我的理解是正确的吗?

英文:

my test code below

type Data struct {
	data int
}

func printData(c chan *Data) {
	time.Sleep(time.Second * 1)
	data := &lt;-c
	for {
		data.data = 100
	}
}

func main() {
	fmt.Println(&quot;Main started...&quot;)
	a := Data{data: 1}
	b := &amp;a

	//create channel
	c := make(chan *Data, 10)
	go printData(c)
	fmt.Println(fmt.Printf(&quot;Value of b before putting into channel %v&quot;, *b))
	c &lt;- b
	for {
		b.data = 20
	}
}

I use go build -race to build the binary, As expected a data race occurs

My doubt is I think the passing pointer is improper because when using the channel with multiple goroutines, the channel has a lock, but a passing pointer to channel, cause goroutines to need to add another lock to protect data race. It's weird

So my understood is right?

答案1

得分: 2

你有一个数据竞争,因为两个例程在没有同步的情况下尝试使用相同的底层对象。如果在创建 goroutine 之前将指针存储到共享对象中,即使没有通道,你也会遇到相同的竞争。如果在传递指针后,在两个 goroutine 中停止使用底层对象,则不会发生竞争。因此,问题不在于通道本身,而在于在两个独立的 goroutine 中使用共享的底层对象。

换句话说,共享的方法并不重要,重要的是存在共享。

CSP 的一个理念是通过通信来共享(即发送需要处理的实际数据),而不是通过共享来通信(即说服可能同时运行的两个独立顺序处理器在共享数据时表现良好)。

当数据本身非常大时,发送指针并立即放弃底层数据可能是一个好策略,以减少数据复制的成本。但这是一种优化技术,只有在性能分析显示数据复制是操作的昂贵部分之后才应该这样做。添加指针间接引用会增加性能成本,必须通过减少通信成本来弥补。在你的特定情况下,要操作的数据只包括一个 int 值,因此添加的指针成本远远超过任何其他减少的成本,只有在算法要求共享数据时才应该共享数据。

英文:

You have a data race because two routines are trying to use the same underlying object without synchronization. You would get this same race without a channel if you stored a pointer to a shared object before creating the goroutine. You would not have a race if, after passing the pointer, you stopped using the underlying object in one of the two goroutines. So it's not the channel itself that is the issue, it is the use of a shared underlying object in two separate goroutines.

The method of sharing is not relevant, in other words. The fact that there is sharing is what is important.

One idea behind CSP is to share by communicating (i.e., send the actual data that need processing) rather than communicating by sharing (i.e., convincing two separate sequential processors that may run concurrently to behave well when when they share the data).

When the data themselves are very large, sending a pointer—and immediately going "hands off" of the underlying data—may be a good strategy, to reduce the cost of the data copy. But this is an optimization technique and should be done only after performance analysis shows that the data-copying is the expensive part of the operation. Adding a pointer indirection adds performance cost, which must be recovered by the reduced communications cost. In your particular case, the data to be operated upon consist of a single int value, so the added pointer cost greatly exceeds any other reduced cost, and you should only share the data if that's required by the algorithm.

huangapple
  • 本文由 发表于 2021年12月23日 09:25:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/70456785.html
匿名

发表评论

匿名网友

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

确定