C#でコルーチン(ユーザースケジュールスレッド)を実装する方法を探しています。 C++を使用する場合、ファイバーを使用していました。インターネット上では、C#にはファイバーが存在しません。同様の機能を取得したいと思います。
C#でコルーチンを実装する「正しい」方法はありますか?
私は、コルーチンごとにこのミューテックスを解放するスケジューラスレッドで単一の実行ミューテックス+ 1を取得するスレッドを使用してこれを実装することを考えました。しかし、これは非常にコストがかかるようです(各コルーチン間でコンテキストの切り替えを強制します)
また、yieldイテレータ機能も見ましたが、理解できるように、内部関数(元のienumerator関数のみ)内でyieldすることはできません。だから、これは私には少し良いことです。
編集:これらを使用できるようになりました: 。netにファイバーAPIがありますか?
Reactive Extensions for .NET をご覧ください。たとえば、 イテレータとyieldを使用してコルーチンをシミュレートできます ステートメント。
ただし、これを読むこともできます SOの質問 も。
新しい.NET 4.5\C#5では、async\awaitパターンがニーズを満たすはずです。
async Task<string> DownloadDocument(Uri uri)
{
var webClient = new WebClient();
var doc = await webClient.DownloadStringTaskAsync(url);
// do some more async work
return doc;
}
詳細については http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 をご覧になることをお勧めします。素晴らしいプレゼンテーションです。
また、 http://msdn.Microsoft.com/en-us/vstudio/gg31636 には素晴らしい情報があります。
古いバージョンの.NETを使用している場合、実稼働環境で使用できるように、Go Liveライセンスを持つ古い.NETで使用できる非同期CTPがあります。 CTPへのリンクを次に示します http://www.Microsoft.com/download/en/details.aspx?displaylang=en&id=998
上記のオプションのいずれかが気に入らない場合は、ここで説明する非同期イテレータパターンに従うことができると思います。 http://www.Microsoft.com/download/en/details.aspx?displaylang=en&id=998
ここ は、スレッドを使用してコルーチンを実装する例です。
だからカンニング。スレッドを使用していますが、一度に実行できるのはそのうちの1つだけです。コルーチンを作成するときは、スレッドを作成し、Monitor.Wait()の呼び出しで終了するハンドシェークを行います。これにより、コルーチンスレッドがブロックされます。ブロックが解除されるまで実行されません。コルーチンを呼び出すときは、呼び出しスレッドがブロックされ、コルーチンスレッドが実行可能な状態で終了するハンドオフを実行します。帰路での同様のハンドオフ。
これらのハンドオフは、他の実装と比較して、かなり高価です。速度が必要な場合は、独自のステートマシンを作成し、このようなコンテキストの切り替えをすべて回避することをお勧めします。 (または、ファイバー対応ランタイムを使用する必要があります。ファイバーの切り替えは非常に安価です。)しかし、表現力豊かなコードが必要な場合、コルーチンにはいくつかの見込みがあると思います。
欠落しているピースをチャネリングする
パイプラインは、golangのチャネルに対して不足している部分です。チャンネルは、実際にgolangを動かすものです。チャネルは、同時実行性の中核ツールです。 C#でコルーチンのようなものを使用しているが、スレッド同期プリミティブ(セマフォ、モニター、インターロックなど)を使用している場合、それは同じではありません。
ほぼ同じ-パイプライン、ただしベイクイン
8年後、.Net Standard(.Net Framework/.Net Core)はパイプラインをサポートしています[ https://blogs.msdn.Microsoft.com/dotnet/2018/07/09/system-io- pipelines-high-performance-io-in-net /] 。パイプラインは、ネットワーク処理に適しています。 Aspcoreは、プレーンテキストスループットリクエストレートの上位11位にランクインしました[ https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext] 。
マイクロソフトは、ネットワークトラフィックとのインターフェイスのベストプラクティスを推奨しています。待機中のネットワークバイト(完了ポートIO)はデータをパイプラインに入れ、別のスレッドはパイプラインからデータを非同期に読み取る必要があります。多くのパイプラインは、バイトストリームのさまざまなプロセスに連続して使用できます。 Pipelineにはリーダーカーソルとライターカーソルがあり、仮想バッファサイズによってライターのバックプレッシャーが発生し、バッファリングのためのメモリの不必要な使用が削減され、通常はネットワークトラフィックが遅くなります。
PipelinesとGoチャンネルにはいくつかの重大な違いがあります。パイプラインはgolangチャンネルとは異なります。パイプラインは、メモリ参照(ポインターを含む)でシグナルを送るためのgolangチャネルではなく、可変バイトを渡すことです。最後に、Pipelinesには同等のselect
がありません。
(パイプラインはSpans [ https://adamsitnik.com/Span/] を使用します。これはしばらく前から存在していましたが、現在.Net Coreで深く最適化されています。スパンはパフォーマンスを大幅に向上させます。ネットコアのサポートにより、パフォーマンスはさらに向上しますが、段階的にしか向上しないため、.Net Frameworkの使用はまったく問題ありません。
したがって、パイプラインは組み込みの標準であり、.Netのgolangチャネルを置き換えるのに役立ちますが、それらは同じではなく、パイプラインが答えではない場合がたくさんあります。
Golangチャンネルの直接実装
.Netチャネルを介してメッセージを渡して、オブジェクトに対する所有権の変更を示すことに注意する必要があります(golangと同様)。これはプログラマーだけが追跡およびチェックできるものであり、間違えた場合は、2つ以上のスレッドが同期なしでデータにアクセスします。
興味があるかもしれません this は、コルーチンの使用を隠すライブラリです。たとえば、ファイルを読み取るには:
//Prepare the file stream
FileStream sourceStream = File.Open("myFile.bin", FileMode.OpenOrCreate);
sourceStream.Seek(0, SeekOrigin.End);
//Invoke the task
yield return InvokeTaskAndWait(sourceStream.WriteAsync(result, 0, result.Length));
//Close the stream
sourceStream.Close();
このライブラリは1つのスレッドを使用してすべてのコルーチンを実行し、真の非同期操作のタスクを呼び出すことができます。たとえば、別のメソッドをコルーチンとして呼び出す(別名、戻り値を返す
//Given the signature
//IEnumerable<string> ReadText(string path);
var result = new Container();
yield return InvokeLocalAndWait(() => _globalPathProvider.ReadText(path), container);
var data = container.RawData as string;