protobuf-net 実装の制限は、基になるストリームを同期的に呼び出すことです。BeginSerialize/EndSerialize や同等の TPL などの非同期 API を提供しないことで、同期ストリーム I/O を待機しているスレッドを拘束せざるを得なくなります。
protobuf-net で非同期メソッドを提供する計画はありますか、またはこの問題を回避する創造的な方法はありますか?
protobuf-net 実装の制限は、基になるストリームを同期的に呼び出すことです。BeginSerialize/EndSerialize や同等の TPL などの非同期 API を提供しないことで、同期ストリーム I/O を待機しているスレッドを拘束せざるを得なくなります。
protobuf-net で非同期メソッドを提供する計画はありますか、またはこの問題を回避する創造的な方法はありますか?
いいえ、それは現在サポートされておらず、大変な作業になります。
私の提案は次のとおりです。非同期APIを使用して自分でデータをバッファリングし、データを取得したら、のようなものを使用MemoryStream
して逆シリアル化します...
私の弁護では、ここで非同期 API を提供する他のシリアライザーを認識していません。特に、低速/非同期ストリームについて話すとき、それは通常「ネットワーク」を意味します。通常、そこで考慮すべき「フレーミング」の問題があります。protobuf-net はフレーミング要件を認識しません...
ネットワーク経由でプロトバフを使用しています。次の解決策は、ブロックしないことを保証するものではありませんが、人生をはるかに良くします:
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);