私はUtf8Json
をたくさん使用しましたが(それは非常に良いことです)、それ以降、いくつかの下位レベルのコードを適合させ、Utf8JsonReader
を直接使用し始めました。
Utf8Json
ライブラリのコードを見ると、最終的にはJsonSerializer.DeserializeAsync
関数を含むSystem.IO Stream
クラスを使用しているため、Stream.ReadAsync
が表示されます。
Utf8JsonReader
を見ると、ReadOnlySequence<byte
を使用しており、非同期関数はありません。
ここを見る https://github.com/dotnet/runtime/issues/29906 言及:
Utf8JsonReaderは再入可能であるため、追加の状態を含むUtf8JsonReaderの非同期ラッパーは、必要なタイプを作成してストリーミング読み取りを実行できる完全同期ヘルパー関数にシェルアウトできます。
私の質問は喜んでです(そして読む時間をありがとう):
なぜ1つのライブラリがメモリバッファーに非同期で(Utf8Json
)、別のライブラリに同期的に(Utf8JsonReader
)アクセスするのでしょうか。
非同期は、ネットワークポートやディスク上のファイルなど、長期にわたるIOには意味があると理解していますが、メモリ上ではオーバーヘッドが有害になるでしょうか? (これがReadOnlySequence<byte>
に非同期機能がない理由ですか?)
Githubサイトの作者は、非同期ラッパーを作成することによって何を意味し、これはどのように見えますか?
私はGitHubの問題から引用された「op」です。 _System.Text.Json
_ APIが最初にプレビューされたとき、非同期にストリームを消費する関数がなかったため、その機能を追加するために、その周りに独自のラッパーを記述する必要がありました。
async
メソッドはout
メソッドであるため、Span
パラメータやスタック割り当て変数(async
およびco)など、スタックに依存する多くの機能を使用できません。割り当てとスコープの終わりの間のある時点で譲歩する可能性があります。ただし、async
メソッドは、これらの機能を自由に使用できる通常の非async
メソッドを自由に呼び出すことができます。これは、スタックの実行中、スタックがその場に留まることが保証されているためです。ヘルパー関数。
私のニーズのために、 最初にStreamSequence
を作成しましたStream
を非同期で消費することを許可しましたが、単一のReadOnlySequenceSegment
にまとめることができるReadOnlySequence
として一度にチャンクをロードすることにより、高性能な方法で同期のみの_Utf8JsonReader
_にコンテンツをフィードします。理論的には、JSONが消費されると、以前のReadOnlySequence
インスタンスを削除することで、ReadOnlySequenceSegment
の先頭でメモリページを再利用できますが、これは実装されていません。
次に、これを、ストリームを解析するステートマシンで使用し、async
を使用するコアStreamSequence
API関数を使用して、一度にストリームからJSONコンテンツのチャンクをロードし、 _ReadOnlySequence<byte>
_の内容をJSONとして解析し、必要な型にシリアル化する責任がある専用(プライベート)非async
関数に渡します。 これは実装から取り除かれたコードです 、人生を邪魔したので、それをより一般化したりクリーンアップしたりすることはありませんでした。 (私はこの返信のためにそれを行うことを考えましたが、投稿に取り掛かるまでに1〜2か月かかる可能性があるので、これはおそらくより良いでしょう。)
これはすべてSTJプレビュー6に基づいています。APIはpreview7で現在の状態にわずかに変更されました。おそらく部分的に、公開された内部状態を見つけたときの混乱について、当時提出したフィードバックが原因です( #29906 =と #29911 )ですが、概念は同じです。 STJのpreview6バージョンをインストールして、このコードをいじってみて、それが正常に機能したら、最新リリースにアップグレードして破損を修正することもできます。すべての場合において、非同期エントリポイントから作業をシェルアウトするための同期ヘルパーメソッドを作成する方法と、その同期コンテキストでSpan
およびcoを自由に使用する方法が明らかであると思います。
現在のバージョンのSTJ APIが実際に_DeserializeAsync<T>
_メソッドを提供していることに注意してください。これは、実際にすべてのコードを置き換えただけです。これは、カスタムの逆シリアル化の必要性が利点を上回らなかったためです(非同期のSTJメソッドはありませんでした)時間は私が書いた時間ですが、JSONストリームに手動で「飛び込み」、_Utf8JsonReader
_と直接やり取りする必要がある場合は、somethingこれらの線に沿って。
(私のStreamSequence
の代わりに同梱されている可能性があることに注意してください。たぶん、急速に変化するASP.NET Core APIに詳しい人がそれにコメントできるかもしれません。)
使用法の更新:StreamSequence
インスタンスの周りにStream
を作成します。これは、それらを結び付ける以外何もしません。 StreamSequence.ReadMoreAsync()
は、スレッドプールをブロックせずに、基になるストリームから非同期にバイトを消費します。プロパティ_StreamSequence.Sequence
_は、これまでのストリームの内容を表す_ReadOnlySequence<byte>
_を公開します。 ReadMoreAsync()
への後続の各呼び出しは、_ReadOnlySequence<byte>
_を別のReadOnlySequenceSegment
で拡張し、より多くのデータを利用できるようにします。 StreamSequence
をインスタンス化し、一度読み取り、次に_StreamSequence.Sequence
_をパーサーヘルパーに渡します。パーサーが操作を続行するためにより多くのデータを必要とする場合は、それを知らせるフラグを返して、非同期エントリポイントにバブルアップし、自由にawait ReadMoreAsync()
を呼び出してから、パーサーを再度呼び出してジョブを続行します。 (必要に応じて状態情報を渡します)。