如何在golang中安全地回收protobuf对象

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

How to safely recycle protobuf objects in golang

问题

我想要重复使用protobuf的消息对象来减少运行时的垃圾回收消耗,但我不确定是否安全。以下是用于测试的示例代码:

test.proto

message Hello{
  uint32  id   = 1;
}

test.pb.go

type Hello struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	ID  uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}

func (x *Hello) Reset() {
	*x = Hello{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_login_api_login_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}
// 其他代码

main.go

func main() {
    // 禁用垃圾回收以测试重新获取相同数据
	gc := debug.SetGCPercent(-1)

	// 作为protobuf对象池
	cache := sync.Pool{New: func() interface{} { return &test.Hello{} }}
	
    // 取出一个对象并使用它
	m1 := cache.Get().(*test.Hello)
	m1.ID = 999
	fmt.Println(&m1.ID) // 打印 999

	// 清空数据并将其放回对象池
	m1.Reset()
	cache.Put(m1)

	// 再次取出一个对象并使用它
	m2 := cache.Get().(*test.Hello)
	fmt.Println(&m2.ID) // 打印 0

	debug.SetGCPercent(gc)
}
英文:

I want to recycle the message object of protobuf to reduce GC consumption at runtime,but I'm not sure whether it's safe.The sample code for testing is as follows:

test.proto

message Hello{
  uint32  id   = 1;
}

test.pb.go

type Hello struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	ID  uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}

func (x *Hello) Reset() {
	*x = Hello{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_login_api_login_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}
// other codes

main.go

func main() {
    // Disable GC to test re-acquire the same data
	gc := debug.SetGCPercent(-1)

	// As a protobuf object pool
	cache := sync.Pool{New: func() interface{} { return &test.Hello{} }}
	
    // Take out an object and use it
	m1 := cache.Get().(*test.Hello)
	m1.ID = 999
	fmt.Println(&m1.ID) // print 999

	// Empty the data and put it back into the object pool
	m1.Reset()
	cache.Put(m1)

	// Take out an object again and use it
	m2 := cache.Get().(*test.Hello)
	fmt.Println(&m2.ID) // print 0

	debug.SetGCPercent(gc)
}

答案1

得分: 1

你展示的代码是安全的。当对象被放入池中后,像这样的池化操作会变得“不安全”,因为在引用对象之后仍然持有对它们的引用。这样做会导致竞态条件或奇怪的错误。因此,这也取决于使用你的对象的代码。

据我所知,协议缓冲区库和gRPC库不会保留对协议缓冲区对象的引用。这样做会破坏很多代码,因为这些库无法知道何时可以安全地重用对象。

因此,只要确保你自己的代码在将对象放入池中后不再使用它们,就应该是安全的。

英文:

The code you show is safe. Pooling like this becomes "unsafe" when references to objects are held after they are put into the pool. You risk race conditions or strange bugs. So it also depends on the code that uses your objects.

The protocol buffers library and gRPC libraries don't hold on to references to protobuf objects, as far as I know. Doing so would break a lot of code since such libraries have no way of knowing when it would be safe to reuse.

So as long as you make sure that your own code doesn't use objects after they are put in the pool, you should be good.

huangapple
  • 本文由 发表于 2022年1月7日 10:16:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/70615855.html
匿名

发表评论

匿名网友

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

确定