我能想到的唯一一个就是不在goroutine之间共享数据 – 父goroutine发送一个对象的深层副本而不是对象本身,所以child goroutine不能改变父对象的内容.这将消耗更多的堆内存,但另一种方法是学习Haskell:P
编辑:同样,是否有任何情况我上面描述的方法仍然可以遇到竞争条件?
B asks A for the currentCount C asks A for the currentCount B sends A (newDataB,currentCount + 1) A stores newDataB at location currentCount+1 C sends A (newDataC,currentCount + 1) A stores newDataC at currentCount + 1 (overwriting newDataB; race condition)
这种竞争条件要求A中的私有可变状态,但是没有可变的共享数据结构,甚至不需要B或C中的可变状态.如果不理解A提供的合同,B或C就无法阻止这种竞争条件.
一旦状态进入等式,即使是哈斯克尔也会遇到这种竞争条件,并且状态很难完全从实际系统中消除.最终你希望你的程序与现实互动,现实是有状态的.维基百科使用STM提供a helpful race condition example in Haskell.
我同意良好的不可变数据结构可以使事情变得更容易(Go并不真正拥有它们).可变副本将一个问题换成另一个问题.您不能无意中更改其他人的数据.另一方面,当您实际上只是更改副本时,您可能会认为您正在更改真实的副本,从而导致出现其他类型的错误.你必须以任何方式理解合同.
但最终,Go倾向于遵循C的并发历史:你为你的代码制定一些所有权规则(比如@ tux21b商品)并确保你总是遵循它们,如果你完美地完成它,那么一切都会很好,如果你犯了错误,那么显然这是你的错,而不是语言.
(不要误解我的意思;我非常喜欢Go.它提供了一些很好的工具来简化并发.它只是没有提供很多语言工具来帮助使并发性正确.这取决于你.这就是说.,tux21b的答案提供了很多好的建议,而竞赛探测器绝对是减少竞争条件的有力工具.它不是语言的一部分,而是关于测试,而不是正确性;它们不是一回事.)
编辑:关于为什么不可变数据结构使事情变得容易的问题,这是你的初始点的扩展:创建一个多方不改变相同数据结构的契约.如果数据结构是不可变的,那么这是免费的……
许多语言都有丰富的不可变集合和类. C让你几乎可以做任何事情. Objective-C具有可变子类的不可变集合(它创建一组不同于const的模式). Scala具有许多集合类型的单独的可变和不可变版本,并且通常的做法是仅使用不可变版本.在方法签名中声明不变性是合同的重要标志.
当你将[]字节传递给goroutine时,无法从代码中知道goroutine是否打算修改切片,也不能自己修改切片.有一种模式出现了,但它们在移动语义之前就像C对象所有权;很多很好的方法,但无法知道哪一个正在使用.每个程序都需要正确执行它是一件至关重要的事情,但是语言没有为您提供好的工具,开发人员也没有使用通用模式.