protobuf-net 实现的一个限制是它同步调用底层流。由于不提供异步 API,例如 BeginSerialize/EndSerialize 或 TPL 等价物,我们被迫占用等待同步流 I/O 的线程。
有没有计划在 protobuf-net 中提供异步方法,或者有什么创造性的方法来解决这个问题?
protobuf-net 实现的一个限制是它同步调用底层流。由于不提供异步 API,例如 BeginSerialize/EndSerialize 或 TPL 等价物,我们被迫占用等待同步流 I/O 的线程。
有没有计划在 protobuf-net 中提供异步方法,或者有什么创造性的方法来解决这个问题?
不,这目前不受支持,而且工作量很大。
我的建议是:使用异步 API 自己缓冲数据,然后当你有数据时,使用类似 a 的东西MemoryStream
来反序列化......
在我的辩护中,我不知道这里有任何其他提供异步 API 的序列化程序。特别是,在谈论慢速/异步流时,这通常意味着“网络”:您通常会在那里考虑“框架”问题;protobuf-net 不会知道您的框架要求...
我在网络上使用 protobuff。虽然以下解决方案不能保证它不会阻塞,但它确实让生活变得更好:
byte[] emptyByteArray = new Byte[0];
await stream.ReadAsync(emptyByteArray, 0, 0);
TaskData d = Serializer.DeserializeWithLengthPrefix<TaskData>(stream, PrefixStyle.Base128);
因为我们在开始反序列化之前确保流上有实际数据,所以只有在流包含部分消息时才会阻塞。
编辑:我们可以使用类似的技巧进行序列化:
MemoryStream mstm = new MemoryStream();
Serializer.SerializeWithLengthPrefix(mstm, data, PrefixStyle.Base128);
await stream.WriteAsync(mstm.GetBuffer(), 0, (int)mstm.Position);
作为奖励,这个确实保证永远不会阻塞。
您可以等待Task.Run
将在线程池中运行同步代码。这不是最有效的解决方案,但比阻塞要好。您甚至可以将自己的提交CancellationToken
给Task.Run
:
await Task.Run(() => Serializer.SerializeWithLengthPrefix(
stream, data, PrefixStyle.Base128), cancellationToken);
或者,您可以使用从我作为异步功能请求的一部分提交给 protobuf-net的JuiceStream 库中提取的相当简单的辅助方法:
await ProtobufEx.SerializeWithLengthPrefixAsync(
stream, data, PrefixStyle.Base128, cancellationToken);
await ProtobufEx.DeserializeWithLengthPrefixAsync<MyType>(
stream, PrefixStyle.Base128, cancellationToken);