この記事は機械翻訳されたものです。
Windows と C++
Windows におけるスレッドと I/O の進化 (機械翻訳)
新しいプロジェクトを開始すると、自分でプログラムを計算-バインドまたは O バインドされるかどうか尋ねるか? あなたする必要があります。 ほとんどの場合、1 つまたは他のいずれかですがわかりました。 データの山を手渡したし、骨材のセットにダウンすべて計算処理中、プロセッサの束を忙しくて analytics ライブラリの作業される可能性があります。 代わりに、コードをデータをネットワーク経由で到着する起こるものを待って、その時間の大半を過ごすかもしれない何かをクリックしてまたはいくつか複雑な 6 本指のジェスチャを実行するユーザー。 この場合、プログラム内のスレッドは、非常に多くを行っていません。 確かに、プログラムが重い I/O と計算の両方にある場合があります。 SQL Server データベース エンジンに思い浮かぶが少ないの今日のコンピューターの典型的なプログラミングします。 多くの場合、あなたのプログラムは他の作業の調整を任務です。 Web サーバーまたは SQL データベースと通信クライアントのいくつかの計算を GPU に押すか、またはユーザーと対話するためのいくつかのコンテンツを提示するかもしれない。 すべてのこれらの異なるシナリオを考えると、どのようにあなたのプログラムが必要ですどのようなスレッド処理機能とどのような同時実行のビルディング ブロック必要または有用です決めていますか? まあ、それは一般的に答え厳しい質問、何か新しいプロジェクトのアプローチを分析する必要がありますです。 ただし、利用できる実用的な選択に基づく情報を決定できるようにスレッド Windows と C++ の進化を理解するは便利です。
スレッドは、もちろん、ない直接提供は一切、ユーザーに値します。 あなたのプログラムは別のプログラムとして 2 回として多くのスレッドを使用する場合はこれ以上素晴らしいです。 それは何をカウントそれらのスレッドのです。 これらのアイデアや方法を説明するためには、スレッド処理は、時間をかけて進化していますで、私いくつかのデータをファイルから読むの例を見てみましょう。 I/O に対する彼らのサポートはほとんど同期、またはブロッキングに向かって対象としているために、C および C++ ライブラリ上を飛ばします、I/O、およびこれは通常はありません関心の単純なコンソール プログラムを構築している場合を除き。 もちろん、何が間違っています。 いくつかの私のお気に入りのプログラムは 1 つの事を行うし、本当によくそれを行うコンソール プログラムです。 それでも、私が移動しますので、非常に興味深いありません。
1 つのスレッド
まず第一に、私は、Windows API と古き良きを開始します — とも適切な名前 — ReadFile 関数。 ファイルの内容の読み取りを開始するに、ファイルへのハンドルを必要とし、このハンドルは、非常に強力なの CreateFile 関数によって提供されます。
auto fn = L"C:\\data\\greeting.txt";
auto f = CreateFile(fn, GENERIC_READ,
FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
ASSERT(f);
例を簡単に維持するには、私だけは ASSERT および VERIFY マクロ プレース ホルダーとしてどこでいくつかのエラーは、さまざまな API 関数によって報告されるエラーを管理するのに処理を追加するのに必要がありますを示すため使用します。 このコード スニペットでは、CreateFile 関数を使用してファイルを作成するのではなくを開きます。 同じ関数は、両方の操作に使用されます。 名前の作成は、カーネル ファイル オブジェクトが作成、され、ファイル システム内のファイルの作成かどうかについては、あまりされるという事実についての詳細です。 パラメーターは、かなり自明とも除いて、2 番目-- フラグとカーネルから必要な I/O 動作の種類を示す属性のセットを指定することができます最後に、この議論に関係のないです。 この場合、ちょうどファイルを通常の同期 I/O 用開かすべきことを示す FILE_ATTRIBUTE_NORMAL 定数を使用します。 それが完了したら、カーネルのロック ファイルを解放する、CloseHandle 関数をコールしてください。 私は私の 2011 年 7 月のコラムで、「C++ と、の Windows API」を紹介したものなどのハンドル ラッパー クラス (msdn.microsoft.com/magazine/hh288076)、トリックを行います。
私は今先に行くし、ファイルの内容をメモリに読み取る ReadFile 関数を呼び出すことができます。
char b[64];
DWORD c;
VERIFY(ReadFile(f, b, sizeof(b), &c, nullptr));
printf("> %.*s\n", c, b);
ご想像のとおり、最初のパラメーターにはファイルへのハンドルを指定します。 次の 2 つは、ファイルの内容を読むことにメモリを記述します。 Readfile 関数は、実際にコピーされたバイト数が要求されたよりも利用可能なより少ないバイトをする必要がありますも返されます。 最後のパラメーターはのみ非同期 I/O の使用され、私は戻ることを一瞬で取得よ。 私は、この単純な例で単にプリント アウト ファイルから実際に読み取られた文字。 当然のことながら、必要に応じて複数回 readfile 関数を呼び出す必要があります。
2 つのスレッド
このモデルの I/O を把握する簡単な確かに非常に有用な多くの小さなプログラムです特にコンソール ベースのプログラム。 しかし、それは非常にうまくスケールしません。 おそらく複数のユーザーをサポートする 2 つの個別のファイルを同時に読み取る必要がある場合は、2 つのスレッドの必要がありますしてよ。 問題はない — that CreateThread 関数です。 次に、簡単な例を示します。
auto t = CreateThread(nullptr, 0,
[] (void *) -> DWORD
{
CreateFile/ReadFile/CloseHandle
return 0;
},
nullptr, 0, nullptr);
ASSERT(t);
WaitForSingleObject(t, INFINITE);
ここで私はステートレス ラムダのコールバック関数ではなく、スレッド プロシージャを表すを使用しています。 ビジュアル C++ 2012 コンパイラ C に準拠している + + 11 の言語仕様でそのステートレス ラムダ関数ポインターに暗黙的に変換する必要があります。 これは便利ですし、x 86 アーキテクチャには、スポーツのさまざまな呼び出し規則に適切な呼び出し規約を自動的に生産することによって Visual C コンパイラを 1 つのより良いか。
CreateThread 関数は、WaitForSingleObject 関数を使用して待機スレッドを表すハンドルを返します。 ファイルの読み取り中、スレッドをブロックします。 この方法で私はタンデムで別の I/O 操作を実行する複数のスレッドを持つことができます。 すべてのスレッドが終了するまで待機する WaitForMultipleObjects を呼び出して可能性があります。 また、カーネル スレッド関連リソースを解放する CloseHandle をコールしてください。
この手法は、ただし、一握りのユーザー ファイルまたはものは何でも使用するプログラムのスケーラビリティのベクトルであるを超えて規模はありません。 明確にするためには、操作がスケールしないしてその複数の優れた読み取りです。 まったく正反対。 しかし、プログラムのスケーラビリティを殺す、スレッドおよび同期のオーバーヘッドします。
1 つのスレッドに戻る
この問題を解決は、アラート可能な I/O 経由で非同期プロシージャ呼び出し (Apc) と呼ばれるものを使用することです。 このモデルでは、プログラムのカーネルは各スレッドに関連付けます Apc キューに依存します。 Apc 両方カーネル モードとユーザー モードの種類があります。 つまり、プロシージャ、または関数のキューに置かれた可能性がありますプログラムでユーザー モードまたはカーネル モード ドライバーのいくつかに帰属します。 後者は、仮想メモリへのアクセスがあるので、いくつかのコードのスレッドのユーザー モード アドレス空間のコンテキストで実行する簡単な方法カーネル ドライバーを許可するためです。 しかし、このトリックもユーザー モード プログラマに利用可能です。 I/O とにかくハードウェアで根本的に非同期であるため (そして、カーネルで)、それはファイルの内容の読み取りを開始し、最終的に完了したらカーネル queue APC を成しています。
まず第一に、フラグと CreateFile 関数に渡された属性ファイルでの操作がカーネルによってシリアル化されていないオーバー ラップ I/O を提供するファイルを許可するように更新しなければなりません。 Windows API では、非同期およびオーバー ラップしたという用語は同義です、彼らは同じことを意味します。 とにかく、FILE_FLAG_OVERLAPPED 定数は、ファイル ハンドルを作成するときに使用する必要があります。
auto f = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
再度、このコード スニペット内の唯一の違いは FILE_FLAG_OVERLAPPED 1 つは、FILE_ATTRIBUTE_NORMAL 定数を置き換えが実行時の違いは巨大であることです。 実際に、カーネルは I/O 完了時キューことができます APC を提供するには、私は代替 ReadFileEx 関数を使用する必要があります。 のみ ReadFileEx ReadFile 非同期 I/O を開始するのには使用できますが、それが完了したときに呼び出す APC を提供できます。 スレッドは先に行きが、I/O はバック グラウンドで完了するまで、おそらく追加の非同期操作を開始、他の作業できます。
C のおかげで再び、+ + 11 と Visual C は、APC を表すラムダ式を使用できます。 トリックは、APC が多くの場合、新しく設定されたバッファーにアクセスしますが、これは、APC にパラメーターの 1 つではないし、だけステートレス ラムダが許可されるため、バッファー変数をキャプチャするラムダを使用できませんです。 ソリューションは、いわばオフ OVERLAPPED 構造体バッファーをハングアップします。 あなたの選択の構造に結果は OVERLAPPED 構造体へのポインターが APC に利用できるため、単にキャストできます。 図 1 に簡単な例を示します。
図 1 APCs とアラート可能な I/O
struct overlapped_buffer
{
OVERLAPPED o;
char b[64];
};
overlapped_buffer ob = {};
VERIFY(ReadFileEx(f, ob.b, sizeof(ob.b),
&ob.o, [] (DWORD e, DWORD c,
OVERLAPPED * o)
{
ASSERT(ERROR_SUCCESS == e);
auto ob = reinterpret_cast<overlapped_buffer *>(o);
printf("> %.*s\n", c, ob->b);
}));
SleepEx(INFINITE, true);
OVERLAPPED ポインターに加えて APC もエラー コードの最初のパラメーターとして提供され、その 2 番目としてコピーされたバイト数。 いくつかの時点で、I/O が完了する、しかし、APC を実行するために、同じスレッドがアラート可能ステートで配置する必要があります。 最も簡単な方法は、できるだけ早く APC キューに登録され、任意の Apc 制御を返す前に実行するスレッドをアクティブ SleepEx 関数です。 もちろん、既にキューに Apc がある場合、スレッドがすべてでは中断できません可能性があります。 それを再開する原因を見つける SleepEx からの戻り値をチェックすることもできます。 無限の代わりにゼロの値は、遅滞なく続行する前に APC キューをフラッシュすることもできます。
SleepEx を使用はない、しかし、すべてが役に立つと Apc は、ポーリングする鉛不謹慎なプログラマを簡単にできるし、は決して良いアイデアです。 このスレッドは 1 つのスレッドからの非同期 I/O を使用している場合は、プログラムのメッセージ ループをまたある可能性あります。 いずれかの方法よりもちょうど Apc を待って、あなたのプログラムをより魅力的なシングル スレッドのランタイムを構築する MsgWaitForMultipleObjectsEx 関数を使用することもできます。 Apc の潜在的な欠点は、彼らはいくつか挑戦的な再入バグは、それを覚えておいて行うのでを紹介することができますです。
プロセッサあたり 1 スレッド
多くの事を行うためにプログラムを見つけるように、残りのプロセッサ コンピューターで何かを待っている周りに座っている間、プログラムのスレッドが実行しているプロセッサが忙しくなっているに気づくかもしれません。 Apc は非同期 I/O を実行するための最も効率的な方法についてですが、彼らは今まで完了操作を開始した同じスレッドで、明らかな欠点があります。 課題、これが拡張できるソリューションを開発することですすべての使用可能なプロセッサ。 おそらく作業スレッド アラート可能なメッセージ ループを持つ数間の調整のあなた自身のこと、デザインの想像かもしれないが、何も実装できる薄手のパフォーマンスとスケーラビリティの I/O 完了ポートに近いため、カーネルのさまざまな部分と深い統合大部分来るでしょう。
APC は単一スレッドで完了する非同期 I/O 操作できますが、完了ポートに I/O 操作を開始する任意のスレッドことができます、結果の任意のスレッドによって処理しています。 完了ポートは、任意の数のオブジェクト ファイル、ソケット、パイプを関連付ける前に作成するカーネル オブジェクトです。 完了ポートは、I/O が完了して、プログラムすることができます任意の利用可能なスレッドにパケットをデキューし必要に応じて処理するとき whereby カーネルが完了パケットをキューにプッシュできるキュー インターフェイスを公開します。 必要に応じてあなた自身完了パケットをキューすることもできます。 主な困難は、周りの混乱の API を得ています。 図 2 関数の使用方法クリアとの関係作り、完了ポートの単純なラッパー クラスを提供します。
図 2 完了ポート ラッパー
class completion_port
{
HANDLE h;
completion_port(completion_port const &);
completion_port & operator=(completion_port const &);
public:
explicit completion_port(DWORD tc = 0) :
h(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, tc))
{
ASSERT(h);
}
~completion_port()
{
VERIFY(CloseHandle(h));
}
void add_file(HANDLE f, ULONG_PTR k = 0)
{
VERIFY(CreateIoCompletionPort(f, h, k, 0));
}
void queue(DWORD c, ULONG_PTR k, OVERLAPPED * o)
{
VERIFY(PostQueuedCompletionStatus(h, c, k, o));
}
void dequeue(DWORD & c, ULONG_PTR & k, OVERLAPPED *& o)
{
VERIFY(GetQueuedCompletionStatus(h, &c, &k, &o, INFINITE));
}
};
メイン混乱です周りの二重の義務を作成IoCompletionPort 関数を実行する、最初に実際に完了ポート オブジェクトを作成して、重複したファイル オブジェクトを関連付けること。 完了ポートは、一度作成され、任意の数のファイルと関連付けられています。 技術的には、単一の呼び出しでは、両方の手順を実行することができますが、これはのみ 1 つのファイルでは、完了ポートを使用する場合に便利ですとが楽しいはどこですか?
完了ポートを作成するときは、唯一の考察スレッド数を示す最後のパラメーターです。 これは、同時に完了パケットをデキューすることができますスレッドの最大数です。 カーネルがプロセッサごとに 1 つのスレッドを許可すると手段をゼロに設定します。
ファイルを追加する技術的アソシエーションと呼ばれます。 注意すべき主な点は、ファイルに関連付けるキーを示すパラメーターです。 余分な情報は、OVERLAPPED 構造体をすることができますよう、ハンドルの端からハングアップすることはできませんので、キーいくつかのプログラムに固有の情報をファイルに関連付けるための方法を提供します。 カーネルがこのファイルに関連する完了パケットをキューするたびに、このキーも含まれます。 ファイル ハンドルがも完了パケットに含まれていないので、これは特に重要です。
私が言ったように、独自の完了パケットをキューことができます。 この場合、指定した値は完全にあなた次第です。 カーネルは気にしない、任意の方法でそれらを解釈しようとしません。 したがって、偽の OVERLAPPED ポインターを提供することができる、正確な同じアドレスは、完了パケットに格納されます。
ほとんどの場合、ただし、非同期 I/O 操作が完了すると完了パケットをキューにカーネルを待っています。 通常プログラムがプロセッサごとに 1 つまたは複数のスレッドを作成し、GetQueuedCompletionStatus、呼び出しまたは私ラッパー関数が無限ループにキューから削除します。 特殊制御完了パケットをキュー可能性があります — スレッドごとに 1 つ — あなたのプログラムは、最後に来る必要があり、これらのスレッドを終了をします。 Apc として、オフ余分な情報を各 I/O 操作に関連付けるには、OVERLAPPED 構造体の詳細をハングすることができます。
completion_port p;
p.add_file(f);
overlapped_buffer ob = {};
ReadFile(f, ob.b, sizeof(ob.b), nullptr, &ob.o);
ここで私は再び元の ReadFile 関数を使用していますが、この場合、私の最後のパラメーターとしての OVERLAPPED 構造体へのポインターを提供しています。 待機中のスレッドは次のとおり完了パケットのデキュー可能性があります。
DWORD c;
ULONG_PTR k;
OVERLAPPED * o;
p.dequeue(c, k, o);
auto ob = reinterpret_cast<overlapped_buffer *>(o);
スレッドのプール
いくつかの時間私の列次のしてきた場合は、Windows スレッド プールの詳細をカバーする昨年 5 ヶ月を過ごしたことを思い出すでしょう。 それも驚きあなたこの同じ作業キューのモデルを提供する、I/O 完了ポートを使用してこの同じスレッド プール API が実装されていることにはスレッドを自分で管理することがなくされません。 また、ホストの機能と直接完了ポート オブジェクトを使用する説得力のある代替する便利さを提供します。 まだ行っていない場合は、Windows スレッド プール API ではスピードを取得、これらの列を読んでお勧めします。 私のオンラインの列のリストで利用可能です bit.ly/StHJtH。
少なくとも、TrySubmitThreadpoolCallback 関数内部でその作業オブジェクトの 1 つを作成し、実行時に直ちに送信コールバックするのにスレッド プールを取得するのに使用できます。 それはこれよりもはるかに簡単に取得していません。
TrySubmitThreadpoolCallback([](PTP_CALLBACK_INSTANCE, void *)
{
// Work goes here!
},
nullptr, nullptr);
もう少しコントロールが必要な場合は、確かに作業オブジェクトを直接作成して、スレッド プール環境およびクリーンアップ グループを関連付けることができます。 これはまた、最高のパフォーマンスを与えます。
もちろん、この議論はオーバー ラップ I/O についてありスレッド プールがちょうどそれのために I/O オブジェクトを提供します。 既にそれ詳細に"プールのタイマーと I/O スレッド"私 2011 年 12 月のコラムで説明したように私はこの上で、多くの時間を費やすことはありません (msdn.microsoft.com/magazine/hh580731)、しかし、 図 3 新しい例を提供します。
図 3 のスレッド プールの I/O
OVERLAPPED o = {};
char b[64];
auto io = CreateThreadpoolIo(f, [] (PTP_CALLBACK_INSTANCE,
void * b, void *, ULONG e, ULONG_PTR c, PTP_IO)
{
ASSERT(ERROR_SUCCESS == e);
printf("> %.*s\n", c, static_cast<char *>(b));
},
b, nullptr);
ASSERT(io);
StartThreadpoolIo(io);
auto r = ReadFile(f, b, sizeof(b), nullptr, &o);
if (!r && ERROR_IO_PENDING != GetLastError())
{
CancelThreadpoolIo(io);
}
WaitForThreadpoolIoCallbacks(io, false);
CloseThreadpoolIo(io);
CreateThreadpoolIo は私へのコールバックをキューに追加のパラメーターを渡すことができますを考えると、私は確かにそれに応じてをすることができるにもかかわらず私はバッファー OVERLAPPED 構造体で、オフにハングアップする必要はありません。 ここで心に留めておくべき主な事柄は非同期 I/O 操作を開始する前に、StartThreadpoolIo を呼び出す必要があり、CancelThreadpoolIo を呼び出す必要がありますですが、I/O 操作は失敗するか完了インライン、いわば。
高速かつ流体のスレッド
スレッド プールの概念を新たな高みに取って、新しい Windows ストアを Windows API のアプリも提供しますスレッド プールの抽象化、はるかに単純な 1 つがはるかに少数の機能を持つ。 幸いにも、何も、あなたのコンパイラやライブラリを適切な代替スレッド プールを使用してからを防ぎません。 フレンドリーな Windows ストア学芸員過去得るでしょうかどうかはまた別の話です。 まだ、Windows の店の apps のためのスレッド プール言及する価値があり、それは Windows ストアを Windows API のアプリによって具体化された非同期パターンを統合します。
滑らかな C + を使用して c++/cli CX 拡張機能は、いくつかのコードを非同期的に実行するため、比較的単純な API を提供します。
ThreadPool::RunAsync(ref new WorkItemHandler([] (IAsyncAction ^)
{
// Work goes here!
}));
構文上、これは非常に簡単です。 私たちも、コンパイラに自動的に C + 生成できる場合この Visual C の将来のバージョンでより簡単になります期待できる c++/cli CX 委任からラムダ — 少なくとも概念的には — 同じ現在の関数ポインターと同様。
それでも、この比較的単純な構文は複雑さを大量を甚だしきます。 高いレベルで ThreadPool は、c# 言語から用語を借りるする静的クラスで、したがってを作成できません。 いくつかのオーバー ロードと runasync メソッドの静的メソッドの提供、それです。 それぞれその最初のパラメーターとして少なくとも、デリゲートを受け取る。 ここでラムダ式とデリゲートを構築するよ。 Runasync メソッド メソッドも非同期操作へのアクセスを提供する IAsyncAction インターフェイスを返します。
利用者の利便性、これはかなりうまく動作し、うまく Windows ストアを Windows API のアプリに浸透している非同期プログラミング モデルに統合します。 できます、たとえば、タスクの並列パターン ライブラリ (PPL) と runasync メソッド メソッドによって返される IAsyncAction インターフェイスでラップ、構成可能性「の追求の効率的とコンポーザブル非同期システム」、私の 9 月と 10 列で説明したことと同様のレベルを達成 (msdn.microsoft.com/magazine/jj618294) とバックに、将来を再開機能」(msdn.microsoft.com/magazine/jj658968)。
ただし、それに役立ち、何が本当にこの一見無害なコードを表しますを実現するためにややありのままのです。 C + 中心に c++/cli CX 拡張は COM およびその IUnknown インターフェイスに基づいてランタイム。 このようなインターフェイス ベースのオブジェクト モデルは、おそらく静的メソッドを提供できません。 オブジェクト、インターフェイス、およびそのオブジェクトを作成するクラス ファクトリのいくつかの並べ替えが場合もする必要があります、確かにあります。
Windows ランタイムは非常に伝統的な COM クラスに似ているランタイム クラスと呼ばれるものを定義します。 古い学校なら、IDL ファイルで、クラスを定義できも具体的には、タスクには適して、MIDL コンパイラの新しいバージョンを介してそれを実行、.winmd メタデータ ファイルと、適切なヘッダーを生成します。
ランタイム クラスのインスタンス メソッドおよび静的メソッドを持つことができます。 彼らは別のインターフェイスを定義しています。 インスタンス メソッドを含むインターフェイスをクラスの既定インターフェイスとなり、静的メソッドを含むインターフェイスは、ランタイム クラスで生成されたメタデータに帰せられます。 この場合、ThreadPool ランタイム クラスはアクティブ化可能な属性が欠けているし、デフォルト インターフェイスを持たないが、作成後は、静的インタ フェースは、問い合わせることができ、それらのない静的なメソッドを呼び出すことができます。 図 4 これが伴う可能性がありますの例を示します。 このほとんどのコンパイラによって生成されるだろうが、それは簡単のためそれが本当に費用の良いアイデアを与える必要があります覚えておいてデリゲートを非同期的に実行する静的メソッドの呼び出し。
図 4 WinRT スレッド プール
class WorkItemHandler :
public RuntimeClass<RuntimeClassFlags<ClassicCom>,
IWorkItemHandler>
{
virtual HRESULT __stdcall Invoke(IAsyncAction *)
{
// Work goes here!
return S_OK;
}
};
auto handler = Make<WorkItemHandler>();
HSTRING_HEADER header;
HSTRING clsid;
auto hr = WindowsCreateStringReference(
RuntimeClass_Windows_System_Threading_ThreadPool,
_countof(RuntimeClass_Windows_System_Threading_ThreadPool)
- 1, &header, &clsid);
ASSERT(S_OK == hr);
ComPtr<IThreadPoolStatics> tp;
hr = RoGetActivationFactory(
clsid, __uuidof(IThreadPoolStatics),
reinterpret_cast<void **>(tp.GetAddressOf()));
ASSERT(S_OK == hr);
ComPtr<IAsyncAction> a;
hr = tp->RunAsync(handler.Get(), a.GetAddressOf());
ASSERT(S_OK == hr);
これは確かに相対的なシンプルさと効率性 TrySubmitThreadpoolCallback 関数の呼び出しからは程遠いです。 生産性のいくらかを与え、コストが正当化されることを決定することに終わる場合でも、あなたが使用する抽象化のコストを理解すると便利です。 簡単にブレーク ダウンさせてください。
WorkItemHandler デリゲート IUnknown ベース IWorkItemHandler インターフェイスは単一の Invoke メソッドを実際にです。 このインターフェイスの実装には、API がなく、コンパイラによって用意されていません。 これはラムダによってキャプチャされた任意の変数を便利なコンテナーを提供し、ラムダの本体は当然コンパイラ生成 Invoke メソッド内に存在するだろうので理にかなって。 この例では私は単に私のための IUnknown を実装する Windows ランタイム ライブラリ (WRL) RuntimeClass テンプレート クラスを頼りにしています。 私は、私の WorkItemHandler のインスタンスを作成するのに便利な Make テンプレート関数を使用できます。 ステートレス ラムダおよび関数ポインターのため、私はさらに静的実装動的割り当てのオーバーヘッドを避けるために IUnknown の no-op 実装を生成するコンパイラを期待します。
ランタイム クラスのインスタンスを作成するには、私は、ロジェを呼び出す必要がありますActivationFactory 関数。 しかし、クラス ID が必要 これは従来の COM の CLSID がタイプ、この場合は、Windows.System.Threading.ThreadPool の完全修飾名ではなくではないことに注意してください。 ここでは私は文字列を実行時にカウントすることを避けるために MIDL コンパイラによって生成される定数の配列を使っています。 十分でない場合、また、HSTRING のバージョンのこのクラス ID を作成する必要 ここで私は、通常の WindowsCreateString 関数とは異なり、ソース文字列のコピーを作成しません、WindowsCreateStringReference 関数を使っています。 便宜上 WRL はまたこの機能アップをラップ HStringReference クラスを提供します。 私は今、IThreadPoolStatics インターフェイスを直接要求して WRL 提供スマート ポインターで結果のポインターを格納する RoGetActivationFactory 関数を呼び出すことができます。
私は今最終的と runasync メソッド メソッドこのインターフェイスでは、私の IWorkItemHandler の実装だけでなく、結果のアクション オブジェクトを表す、IAsyncAction スマート ポインターのアドレスを提供する呼び出すことができます。
それは持っていますし、このスレッド プール API が近くのどこかに提供していないこと驚くべきおそらく機能性と柔軟性の量、コア Windows スレッド プール API または同時実行ランタイム提供します。 利点の C + + CX とランタイム クラスです、しかし、実現したプログラムとランタイム自体の境界に沿って。 C++ プログラマは、あなたがそれを必要なときにまったく新しいプラットフォーム Windows 8 ではないし、伝統的な Windows API はまだあなたの処分で、感謝してすることができます。
Kenny Kerr は、ネイティブの Windows 開発に情熱を注いでいるソフトウェア設計者です。彼のブログ ( kennykerr.ca、英語) をご覧ください。
この記事のレビュー、次技術専門家のおかげで:ジェームズ p. McNellis