私はアプリケーション(Win64、C++)をより非同期にすることで改善しています。私はConcurrency Runtimeを使用しています。これは今まで私にとって素晴らしい仕事でした。同時実行ランタイム用のタスクローカル変数の実装
アプリケーションは、基本的に、さまざまな「ジョブ」変換データを実行します。各ジョブが何をするかを追跡するために、特定のサブシステムには、ジョブが実行する特定の操作を追跡するためのコードが組み込まれています。以前は、現在実行中のジョブを表す単一のグローバル変数を使用して、コンテキスト情報を呼び出しチェーン全体に渡すことなく追跡情報を登録することができました。各ジョブはConcRTを使用してジョブ自体を並列化することもできます。これはすべてうまくいく。
私はトップレベルのジョブを並行して実行できるように、アプリケーションをリファクタリングしています。各ジョブはConcRTタスクとして実行され、トラッキングが必要なジョブ以外のすべてのジョブでうまく機能します。
私が基本的に必要とするのは、いくつかのコンテキスト情報をタスクに関連づけ、そのタスクによって生成された他のタスクへのフローを持つ方法です。基本的には、私は "タスクローカル"変数が必要です。
ConcRTを使用すると、コンテキスト情報を格納するためにスレッドローカルを使用するだけでは、ジョブがConcRTを使用して他のジョブを生成することはできません。
私の現在のアプローチは、起動時にいくつかのスケジューラインスタンスを作成し、各ジョブをそのジョブ専用のスケジューラに起動することです。私はConcurrency::CurrentScheduler::Id()
関数を使用して、コンテキストを把握するためのキーとして使用できる整数IDを取得できます。これは動作しますが、組み立て時にConcurrency::CurrentScheduler::Id()
をシングルステップ実行すると、複数の仮想関数呼び出しと安全チェックを実行するため、多少のオーバーヘッドが追加されるため、ややこしいことになります。このルックアップは極端にいくつかのケースでは高いレート。
だから、これを達成するための良い方法がありますか?私は非常に小さなオーバーヘッドで取り出すことができる現在のスケジューラ/スケジューラグループ/タスクと単一のコンテキストポインタを関連付けることを可能にするファーストクラスのTaskLocal/userdataメカニズムを持つことが大好きでした。
ConcurTスレッドが新しいタスクを取得するたびに呼び出されるフックは私がスケジューラ/スケジュールグループIDを取得し、最小限のアクセスオーバーヘッドのためにスレッドローカルに格納することができるので理想的です。ああ、私はそのようなフックを登録する方法は全く見当たりませんし、PPL /エージェントのカスタムスケジューラクラスを実装することはできないようです(this article参照)。
はい、明らかな解決策です。それは基本的に私たちのコードの大部分で行います。しかし、コードのサブセットについては、過度の冗長さを招くため、オプションではありません。 基本的に、セッター/ゲッターを持つコード生成データオブジェクトクラスがあります。ゲッターは、データの依存関係を追跡するために、使用されたことを登録するためにオプションでインストルメントすることができます。コンテキストオブジェクトをこれらすべてのゲッターに渡さなければならず、コンテキストをすべてのコールチェーンに振り向けなければならないのは非常に面倒です。 基本的にスケジューラIDを使用してスケジューラローカル変数を実装すると、優れたパフォーマンスで動作します。 –
@StefanBoberg:実際にシングルトンではないシングルトンの代替モデルがあります。値を「スタック」することができます。新しい値をスタックにプッシュすることができます。値はポップされ、古い値が復元されるまで、Singletonの値になります。この変数の親スレッドのインスタンスの値で初期化されたスレッドローカル変数を持つことで、これを環境に適応させることができます。 – Omnifarious