问题描述
在工作和生成protobuf存根的过程中,我偶然发现了这个有趣的问题。
每当我尝试按值复制消息的结构时,都会收到此警告:
调用state.world.script.HandleEvent复制锁值:王位/服务器/消息。PlayerDialogeStatus包含google.golang.org/protobuf/internal/impl.MessageState包含sync.Mutex复制锁
虽然我理解为什么按值复制互斥锁是错误的,但我开始想知道为什么它们甚至都放在那儿。
因此,我的问题是:为什么go生成的protobuf文件包含放在消息结构上,尤其是在MessageState
结构上的互斥锁?
或者:在生成的protobuf消息结构上找到的MessageState
结构中放置互斥锁的目的是什么?
解决方法
impl.MessageState仅嵌入具体消息中,而不嵌入生成的实现原始消息的结构中。
它专门嵌入了三个pragmas:)({});
/** added this: */
if ( typeof define === "function" && define.amd ) {
define( "idb",[],function() {
return { openDB:idb.openDB,deleteDB:idb.deleteDB,wrap:idb.wrap,unwrap:idb.unwrap}
});
}
,NoUnkeyedLiterals
和DoNotCompare
。
最后一个DoNotCopy是DoNotCopy
的零大小数组。唯一的目的是让sync.Mutex
大声抱怨浅拷贝,如评论中所述:
DoNotCopy可以嵌入到结构中,以防止浅拷贝。 这不依赖于Go语言功能,而是一种特殊情况 在兽医检查器中。
所有摘要:go vet
不应被复制,并且互斥锁仅用于复制。如果这样做,是因为您使用了错误的方式。
据我所知,Go protobuf API 包含 DoNotCopy 互斥锁的原因有以下三个:
- 维护者可能希望在未来以一种不适用于浅拷贝的方式更改内部表示。
- 将原子访问和非原子访问混合到同一内存理论上是不安全的。 protobuf 结构包含一个内部字段,通常使用原子访问读取和写入。对消息调用
msg.Marshal()
,然后用*msg = MyMessage{...}
覆盖它会混合原子访问和非原子访问。即使这适用于今天在 x86 上的实现,也不能保证这将适用于未来的其他系统。 (见a long Go issue about this)。 - 如果您对消息调用
ProtoReflect()
,然后覆盖该消息,它将崩溃,因为 ProtoReflect() 结果依赖于内部反射指针 (original issue):
d := &durationpb.Duration{Seconds: 1}
protoreflectMessage := d.ProtoReflect()
fmt.Printf("protoreflectMessage.Interface()=%v\n",protoreflectMessage.Interface())
*d = durationpb.Duration{Seconds: 2}
fmt.Printf("protoreflectMessage.Interface()=%v\n",protoreflectMessage.Interface())
这会崩溃:
protoreflectMessage.Interface()=seconds:1
panic: invalid nil message info; this suggests memory corruption due to a race or shallow copy on the message struct