golang race condition - 在2个goroutines中编组到XML

时间:2015-02-06 19:41:15

标签: go

当我尝试在2个或更多的GoRoutine中编组Struct to XML时,我正在获得数据竞争条件。

示例主程序:http://play.golang.org/p/YhkWXWL8C0

我相信xml:"members>member"造成这种情况。如果我把它改成正常那么一切正常。任何想法为什么go-1.4.x版本这样做。

Family struct {
    XMLName xml.Name `xml:"family"`
    Name    string   `xml:"famil_name"`
    Members []Person `xml:"members>member"`
    //Members []Person `xml:"members"`
}

去运行-race data_race.go给我

2015/02/06 13:53:43  Total GoRoutine Channels Created 2
2015/02/06 13:53:43 <family><famil_name></famil_name><members><person><name>ABCD</name><age>0</age></person><person><name>dummy</name><age>0</age></person></members></family>
==================
WARNING: DATA RACE
Write by goroutine 6:
  runtime.slicecopy()
      /usr/local/go/src/runtime/slice.go:94 +0x0
  encoding/xml.(*parentStack).push()
      /usr/local/go/src/encoding/xml/marshal.go:908 +0x2fb
  encoding/xml.(*printer).marshalStruct()
      /usr/local/go/src/encoding/xml/marshal.go:826 +0x628
  encoding/xml.(*printer).marshalValue()
      /usr/local/go/src/encoding/xml/marshal.go:531 +0x1499
  encoding/xml.(*Encoder).Encode()
      /usr/local/go/src/encoding/xml/marshal.go:153 +0xb8
  encoding/xml.Marshal()
      /usr/local/go/src/encoding/xml/marshal.go:72 +0xfb
  main.ToXml()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:51 +0x227
  main.func·001()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:61 +0x74

Previous read by goroutine 5:
  encoding/xml.(*parentStack).trim()
      /usr/local/go/src/encoding/xml/marshal.go:893 +0x2ae
  encoding/xml.(*printer).marshalStruct()
      /usr/local/go/src/encoding/xml/marshal.go:836 +0x203
  encoding/xml.(*printer).marshalValue()
      /usr/local/go/src/encoding/xml/marshal.go:531 +0x1499
  encoding/xml.(*Encoder).Encode()
      /usr/local/go/src/encoding/xml/marshal.go:153 +0xb8
  encoding/xml.Marshal()
      /usr/local/go/src/encoding/xml/marshal.go:72 +0xfb
  main.ToXml()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:51 +0x227
  main.func·001()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:61 +0x74

Goroutine 6 (running) created at:
  main.AsyncExecute()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:67 +0x15d
  main.main()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:80 +0x2bf

Goroutine 5 (finished) created at:
  main.AsyncExecute()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:67 +0x15d
  main.main()
      /Users/kadalamittai/selfie/go/src/github.com/ivam/goal/command/data_race.go:80 +0x2bf
==================

1 个答案:

答案 0 :(得分:2)

这看起来像Go 1.41库中的一个错误。我已报告as a bug。希望它应该得到修复。我将在下面留下分析以供参考。


由于使用getTypeInfo()返回结构的类型描述,因此发生了隐含的共享值。为了提高效率,它似乎是全局缓存状态。 XML编码器的其他部分采用此状态的组件并传递它。由于共享值的一个组件上有一个切片append,似乎发生了无意的突变。

作为数据竞赛来源报告的p.stack属性来自typeInfo共享值的一部分,其中tinfo.parents切片被注入{ {3}}。这最终会发生共享,并且有可能进行读写,因为稍后会在切片上发生appends,并且可以在底层数组上进行突变。

可能应该发生的事情是切片应该是容量限制的,这样任何潜在的append都不会对共享数组值进行写操作。

也就是说,编码器库的第897行可能会改为:

897        s.stack = parents[:split]

为:

897        s.stack = parents[:split:split]

纠正问题。