golang竞态条件 – 在两个goroutine中进行XML编组

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

golang race condition - marshaling to XML in 2 goroutines

问题

当我尝试在两个或更多的GoRoutine中将结构体编组为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"`
}

运行go run -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
==================
英文:

i am getting data race condition when i try to marshal Struct to XML in a GoRoutine of 2 or more.

Sample main program : http://play.golang.org/p/YhkWXWL8C0

i believe xml:&quot;members&gt;member&quot; causing this . if i change it to normal then all works fine. any thoughts why go-1.4.x version doing that.

Family struct {
    XMLName xml.Name `xml:&quot;family&quot;`
    Name    string   `xml:&quot;famil_name&quot;`
    Members []Person `xml:&quot;members&gt;member&quot;`
    //Members []Person `xml:&quot;members&quot;`
}

go run -race data_race.go giving me

2015/02/06 13:53:43  Total GoRoutine Channels Created 2
2015/02/06 13:53:43 &lt;family&gt;&lt;famil_name&gt;&lt;/famil_name&gt;&lt;members&gt;&lt;person&gt;&lt;name&gt;ABCD&lt;/name&gt;&lt;age&gt;0&lt;/age&gt;&lt;/person&gt;&lt;person&gt;&lt;name&gt;dummy&lt;/name&gt;&lt;age&gt;0&lt;/age&gt;&lt;/person&gt;&lt;/members&gt;&lt;/family&gt;
==================
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&#183;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&#183;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

得分: 2

这看起来像是Go 1.41库中的一个错误。我已经将其报告为一个错误。希望它能得到修复。我将保留下面的分析供参考。


问题出在使用getTypeInfo()时存在一个隐式的共享值,它返回结构体的类型描述。为了提高效率,它似乎是全局缓存状态。XML编码器的其他部分会获取这个状态的组成部分并传递它。似乎由于共享值的一个组成部分上进行了切片append,导致了意外的变异。

报告数据竞争的p.stack属性源自typeInfo共享值的一部分,其中在第821行注入了一个tinfo.parents的切片。这就是共享发生的地方,可能会进行读取和写入,因为后面的切片上会进行append操作,这可能会对底层数组进行变异。

应该采取的措施是限制切片的容量,这样任何潜在的append操作都不会对共享数组值进行写入。

也就是说,编码器库中的第897行可能应该从:

897        s.stack = parents[:split]

改为:

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

以修复这个问题。

英文:

This looks like a bug in the Go 1.41 library. I've reported it as a bug. Hopefully it should get fixed. I'll leave the analysis below for reference.


What's happening is that there's an implicit shared value due to the use of getTypeInfo() which returns a type description of the struct. For efficiency, it appears to be globally cached state. Other parts of the XML encoder take components of this state and pass it around. It appears that there's an inadvertent mutation happening due to a slice append on a component of the shared value.

The p.stack attribute that's reporting as the source of the data race originates from a part of the typeInfo shared value, where a slice of tinfo.parents gets injected on line 821. That's ultimately where the sharing is happening with the potential for read and writing, because later on there are appends happening on the slice, and that can do mutation on the underlying array.

What should probably happen instead is that the slice should be capacity-restricted so that any potential append won't do a write on the shared array value.

That is, line 897 of the encoder library could probably be be changed from:

897        s.stack = parents[:split]

to:

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

to correct the issue.

huangapple
  • 本文由 发表于 2015年2月7日 03:41:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/28373497.html
匿名

发表评论

匿名网友

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

确定