この記事は機械翻訳されたものです。

Windows と C++

Windows と C++ での同期の進化 (機械翻訳)

Kenny Kerr

 

Kenny Kerr並行ソフトウェアを書いて最初に始めたときは、C++ の同期のサポートがなかった。 Windows 自体は、すべてのカーネルで実装された、同期プリミティブのほんの一握りがあった。 その場合はミュー テックスのために使用プロセス間で同期するために必要な場合を除き、クリティカル セクションを使用する傾向。 一般的な用語、これらの両方のロック、またはオブジェクトをロックします。

ミュー テックス名「排他」別の名前を同期の概念からを取る。 スレッド 1 つだけ一度にいくつかのリソースにアクセスできることの保証を指します。 クリティカル セクションは、このようなリソースにアクセスする可能性がありますコードの実際のセクションからその名を取ります。 正当性を確保するには、1 つだけのスレッドを同時にこのコードのクリティカル セクションを実行できます。 これら 2 つのロック オブジェクト機能が異なるが、それを持っている彼らがしていることを覚えて役立つだけロック、彼らは両方相互排他の保証を提供し、両方がコードのクリティカル セクションを分離するために使用できます。

今日の同期の風景は劇的に変わりました。 C++ プログラマのための選択の茄多があります。 Windows は今もっとたくさんの同期機能をサポートし、C++ そのものは、長い最後に、それらのために C をサポートするコンパイラを使用して同時実行と同期の機能の興味深いコレクションを提供 + + 11 標準。

今月のコラムで Windows と C++ での同期の状態を探索するつもり。 Windows 自体によって提供される同期プリミティブの見直しを開始し、標準 C++ ライブラリによって提供される選択肢を検討します。 移植性があなたの主な関心事の場合は、新しい C++ ライブラリの追加機能は、非常に魅力的ななります。 ただし、移植性はそれほど重要です、パフォーマンスが最も重要、どの Windows でおなじみの取得をする場合は、現在の提供が重要になります。 レッツ ダイビング右で。

クリティカル セクション

最初にクリティカル セクション オブジェクトです。 このロックは大きく無数のアプリケーションで使用されるが、卑しむべき歴史を持っています。 クリティカル セクションを使用して始めたとき、彼らは本当に簡単だった。 このようなロックを作成するには、すべてあなたが必要な CRITICAL_SECTION 構造を割り当て、それを準備する InitializeCriticalSection 関数を呼び出すことでした。 この関数は失敗することはできませんを意味して、値を返しません。 戻って当時、ただし、この関数のさまざまなシステム リソース、特にカーネル イベント オブジェクトを作成する必要がある、それは非常に低いメモリの状況でこの、提起されて構造化例外の結果として失敗することは不可能だった。 ほとんどの開発者はこの可能性は無視まだ、これはむしろ稀だった。

COM の人気で、クリティカル セクションの使用は飛躍的に多くの COM クラス同期では、クリティカル セクションは使用は多くの場合の話をするにはない実際の競合にほとんどありませんでしたので。 マルチプロセッサ コンピューターが普及すると、クリティカル セクションは、ロックの取得を待機している間ユーザー モードでスピンと簡潔にするため、クリティカル セクションの内部のイベントよりも少ない使用を見た。 小さなスピン カウントは、多くの短い期間の競合はパフォーマンスが大幅に向上、カーネル遷移を避けることが意味しました。

この時間のまわりいくつかのカーネル開発者は自分の存在を必要とするには、十分な競合になるまでのクリティカル セクション イベント オブジェクトの作成を延期する場合劇的に Windows のスケーラビリティを改善できることを実現しました。 開発者この InitializeCriticalSection 今いないが失敗する可能性がありますが、(ロックの所有を待ちます使用) EnterCriticalSection 関数はもはや信頼性の高いだったものを実現するまでこれは良いアイデアのように思えた。 さまざまなクリティカル セクションを正しく使用して、無数のアプリケーションを壊すことはほとんど不可能と作った失敗条件を導入したためこれに簡単に開発者によって見落とされる可能性がないです。 それでも、スケーラビリティ wins が見落とされることができませんでした。

カーネル開発者キー付きイベントと呼ばれる、文書化され、新しいカーネル イベント オブジェクトの形でのソリューションを最後に到着。 あなたは少しそれについて本では、「Windows の内部、」マークは大腸菌によって読むことができます。 ルシノビッチ、デビッド a. ソロモンとアレックス ・ イオネスク (マイクロソフト プレス、2012年) が基本的には、イベント オブジェクトのすべてのクリティカル セクション必要はなく、1 つのキー付きイベントすべての重要なセクションのシステムで使用できます。 キー付きイベント オブジェクトがちょうどそれのために動作します。それはアドレス空間は自然のポインター サイズの識別子には、キーに依存してローカル。

確かに排他的に、キー付きイベントを使用してクリティカル セクションを更新するように誘惑されたが、多くのデバッガーやその他のツールは、クリティカル セクションの内部に依存するので、定期的にイベント オブジェクトを割り当て、カーネルが失敗した場合キー付きイベントのみ最後の手段として使用されました。

無関係の歴史の多くが、キー付きイベントをパフォーマンスの事実は、Windows Vista の開発サイクル中に大幅に向上したこのリード導入への完全に新しいロック オブジェクトが簡単かつ高速いたように聞こえるかもしれない-しかし、詳細は 1 分で。

クリティカル セクション オブジェクト現在障害により低いメモリ条件を免除されているように、実際に使用することは非常に簡単です。 図 1 単純なラッパーを提供します。

図 1 クリティカル セクション ロック

class lock
{
  CRITICAL_SECTION h;
  lock(lock const &);
  lock const & operator=(lock const &);
public:
  lock()
  {
    InitializeCriticalSection(&h);
  }
  ~lock()
  {
    DeleteCriticalSection(&h);
  }
  void enter()
  {
    EnterCriticalSection(&h);
  }
  bool try_enter()
  {
    return 0 != TryEnterCriticalSection(&h);
  }
  void exit()
  {
    LeaveCriticalSection(&h);
  }
  CRITICAL_SECTION * handle()
  {
    return &h;
  }
};

私はすでに述べた EnterCriticalSection 関数は、非ブロッキングの代替手段を提供します、TryEnterCriticalSection 関数によって補完されます。 LeaveCriticalSection 関数ロック、および DeleteCriticalSection、道に沿って割り当てられる可能性があります任意のカーネル リソースを解放します。

クリティカル セクションが合理的な選択です。 それは非常に実行同様にカーネルの移行およびリソースの割り当てを避けるために試みます。 それでも、それはその歴史とアプリケーションの互換性のために運ぶ必要がありますの荷物のビットを持っています。

ミュー テックス

ミュー テックス オブジェクトは、真のカーネル同期オブジェクトです。 クリティカル セクションとは異なり、ミュー テックスのロックは常にカーネルが割り当てリソースを消費します。 利点は、もちろん、カーネル、その意識がロックのためのプロセス間の同期を提供することができます。 カーネル オブジェクトとしては、それは通常の属性を提供します — 名など — オブジェクトを他のプロセスから開くまたはちょうどロックをデバッガーで識別するために使用できます。 オブジェクトへのアクセスを制限するアクセス マスクを指定することもできます。 プロセス内のロックとしてそれやり過ぎ、少し複雑を使用して多くの遅いのです。 図 2 は、効果的にローカルのプロセスである無名のミュー テックスを単純なラッパーを提供します。

図 2 相互排他ロック

#ifdef _DEBUG
  #include <crtdbg.h>
  #define ASSERT(expression) _ASSERTE(expression)
  #define VERIFY(expression) ASSERT(expression)
  #define VERIFY_(expected, expression) ASSERT(expected == expression)
#else
  #define ASSERT(expression) ((void)0)
  #define VERIFY(expression) (expression)
  #define VERIFY_(expected, expression) (expression)
#endif
class lock
{
  HANDLE h;
  lock(lock const &);
  lock const & operator=(lock const &);
public:
  lock() :
    h(CreateMutex(nullptr, false, nullptr))
  {
    ASSERT(h);
  }
  ~lock()
  {
    VERIFY(CloseHandle(h));
  }
  void enter()
  {
    VERIFY_(WAIT_OBJECT_0, WaitForSingleObject(h, INFINITE));
  }
  bool try_enter()
  {
    return WAIT_OBJECT_0 == WaitForSingleObject(h, 0);
   }
  void exit()
  {
    VERIFY(ReleaseMutex(h));
  }
  HANDLE handle()
  {
    return h;
  }
};

ロック、CreateMutex 関数を作成し、共通の CloseHandle 関数、プロセス終了処理を効果的にカーネルでのロックの参照カウントをデクリメントします。 ロック所有権を待ってはチェックし、必要に応じて、さまざまなカーネル オブジェクトのシグナル状態に待機する、汎用の WaitForSingleObject 機能します。 2 番目のパラメーターは、ロックの取得を待機して呼び出しスレッドをブロックする必要がありますどのくらいの期間を示します。 無限の定数である — は驚くほど — が無期限の待機を中にゼロの値がすべてで待ってからスレッドを防止し、無料の場合は、ロックを取得します。 最後に、ReleaseMutex 関数は、ロックを解放します。

ミュー テックス ロック大きなハンマーの力、多くのですが、それはコスト パフォーマンスと複雑さに来る。 ラッパーの図 2 それが失敗することができますがほとんどの場合ミュー テックス ロックを失格パフォーマンスへの影響の可能な方法を示すためのアサーションと散らばっています。

イベント

私は、高パフォーマンスのロックについて話をする前に、1 つを私はすでに触れました、1 つのより多くのカーネル同期オブジェクトを導入する必要が。 あります 実際には、ロックが、相互排他を直接実装する機能していないことには、イベント オブジェクトはスレッド間で作業の調整のため極めて重要です。 実際には、それは内部的にクリティカル セクション ロックによって使用される同じオブジェクトです — とそれに、それ、効率的かつスケーラブルな方法ですべての種類の同時実行パターンを実装する場合に便利です。

CreateEvent 関数イベントが作成されますと — にミュー テックスのような — カーネル オブジェクトのリリース ハンドルを CloseHandle 関数を閉じます。 実際には、ロックではないので、取得/リリース セマンティクスをもたない。 むしろ、それは多くのカーネル オブジェクトがシグナル伝達機能の権化です。 どのようにシグナル伝達の作品では、イベント オブジェクトは 2 つの状態のいずれかで作成できることに感謝する必要があります理解します。 CreateEvent の真の 2 番目のパラメーターを渡す場合は、結果のイベント オブジェクトは手動リセット イベントといいます。 それ以外の場合、自動リセット イベントが作成されます。 手動リセット イベントは、手動で設定およびオブジェクトのシグナル ステートにリセットが必要です。 SetEvent と ResetEvent 関数はこの目的のために提供されます。 自動リセット イベント自動­自動的にリセットされます (からの変更が非シグナル シグナル状態) 待機中のスレッドの解放のとき。 だから 1 つのスレッドは任意の数のスレッドを調整する必要がある場合手動リセット イベントは便利であるに対し 1 つのスレッドを 1 つの他のスレッドを調整する必要があるときに自動リセット イベントは便利です。 自動リセット イベントを SetEvent を呼び出すリリースする予定で最も 1 つのスレッド、手動リセット イベントを呼び出すことに対しすべての待機中のスレッドを解放します。 ミュー テックスのように、イベントがシグナル状態になるまでの待機、WaitForSingleObject 関数を実現します。 図 3 どちらのモードで構築することができます、名前のないイベントを単純なラッパーを提供します。

図 3 イベントの信号

class event
{
  HANDLE h;
  event(event const &);
  event const & operator=(event const &);
public:
  explicit event(bool manual = false) :
    h(CreateEvent(nullptr, manual, false, nullptr))
  {
    ASSERT(h);
  }
  ~event()
  {
    VERIFY(CloseHandle(h));
  }
  void set()
  {
    VERIFY(SetEvent(h));
  }
  void clear()
  {
    VERIFY(ResetEvent(h));
  }
  void wait()
  {
    VERIFY_(WAIT_OBJECT_0, WaitForSingleObject(h, INFINITE));
  }
};

slim reader/writer ロック

スリムなリーダー/ライター (SRW) ロックの名前は一口は、かもしれないが、手術の単語は「スリム」です。プログラマこのロックのため共有読者とおそらく彼らは必要があるときは、クリティカル セクション、これやり過ぎであることを考えて、排他的な作家を区別する能力を見落とす可能性があります。 それが判明して、これに対処する最も簡単なロックですもはるか最速、そしてあなた確かにそれを使用するために読者を共有する必要はありません。 それは主ユーザー モードで実装されているため、スレッドがオフに寝て良いだろうの競合がある場合のみ、カーネルにフォールバックだけそれ、効率的なキー付きイベント オブジェクトにも頼るのでこのスピーディーな評判があります。 もう一度、クリティカル セクションとミュー テックス オブジェクトが再帰またはプロセス間のロックなど、必要があります追加の機能を提供が、多くの場合、すべての必要な内部使用のための高速かつ軽量ロックです。

このロック、さきほどキー付きイベント排他的依存し、など、さまざまな機能を提供するにもかかわらず、非常に軽量です。 SRW ロックのみ、ポインター-が必要です­サイズのカーネルではなく、呼び出し元のプロセスに割り当てられるストレージの量。 このため、InitializeSRWLock、初期化関数が失敗することはできず、単ロックは使用前に適切なビット パターンを含む保証されます。

ロック所有権は、いずれかを取得を使用して達成されるを待って­SRWLockExclusive 機能、いわゆるライター ロックまたはリーダー ロックの AcquireSRWLockShared 関数を使用します。 ただし、排他的で共有用語はより適切です。 対応するリリースは、試して - 関数両方排他的に期待して共有モードの一つとして取得します。 図 4 の排他モード SRW ロックのための単純なラッパーを提供します。 必要に応じて共有モード機能を追加するは難しいでしょう。 無料のリソースはないためにデストラクターがないことに注意してください。

図 4 SRW ロック

class lock
{
  SRWLOCK h;
  lock(lock const &);
  lock const & operator=(lock const &);
public:
  lock()
  {
    InitializeSRWLock(&h);
  }
  void enter()
  {
    AcquireSRWLockExclusive(&h);
  }
  bool try_enter()
  {
    return 0 != TryAcquireSRWLockExclusive(&h);
  }
  void exit()
  {
    ReleaseSRWLockExclusive(&h);
  }
  SRWLOCK * handle()
  {
    return &h;
  }
};

条件変数

私は導入する必要が最終的な同期オブジェクト条件変数です。 これは、おそらくのほとんどのプログラマは慣れているだろうものです。 私はしかし、カ月での条件変数の新たな関心を気づいています。 これは何かと C を必要があります + + 11、しかし、アイデアは新しくないし、Windows ではこの概念いくつか時間を回避されているのサポートします。 モニターのクラスにマージされたが確かに、Microsoft .NET Framework 条件変数パターン非常に最初のリリース以来、いくつかの方法におけるその有用性を制限するサポートしています。 しかし、この新たな関心また Windows Vista で導入する状態変数を許可素晴らしいのキー付きイベントのおかげで、彼らだけ以来改善しています。 条件変数は単なる同時実行のパターンあり、したがって、他のプリミティブを実装できますが、OS でのインクルー ジョンは驚くべきパフォーマンスを達成することができます、このようなコードの正確さを確保することからプログラマを解放しますがありません。 確かに、OS の同期プリミティブを採用している場合は、OS 自体の助けなしに同時実行パターンをいくつかの正当性を確保することはほとんど不可能です。

条件変数のパターンは、それについては非常に一般的です。 プログラムは、いくつかの条件が続行する前に満たされるを待つ必要があります。 この条件の評価には、いくつかの共有の状態を評価するために、ロックの取得が含まれます。 ただし、条件はまだ満たされていない場合は、ロックを解放する、条件を満たすためにいくつかの他のスレッドを許可する必要があります。 評価スレッドはロックを取得する前にもう一度条件が満たされるまでは、待つ必要があります。 ロックを再取得すると、条件が再び明らかな競争条件を避けるために評価する必要があります。 これを実装するが、以上のようだがあるので、実際には、難しく心配する他の落とし穴 — と効率的な方法で実装することはまだ難しい。 次の擬似コードは、問題を示します。

lock-enter
while (!condition-eval)
{
  lock-exit
  condition-wait
  lock-enter
}
// Do interesting stuff here
lock-exit

しかし、この図でもそこにある微妙なバグ。 正しく機能するためには、条件時にロックが終了が、ロック、決して解放されるだろうのでやってので動作しないと前に待たなければなりません。 アトミック 1 つのオブジェクトを解放し、別を待機する機能は、Windows カーネル オブジェクトは、特定する SignalObjectAndWait 関数を提供することが重要です。 しかし、SRW ロックはユーザー モードで主に住んでいるので、別のソリューションが必要です。 条件変数を入力します。

SRW ロックのような条件変数は単一ポインター サイズの量だけストレージを占め、フェイル セーフの InitializeConditionVariable 関数で初期化されます。 SRW ロックにはリソースを解放するには、条件変数が不要になったときに単に、メモリをクリアすることができますように。

条件自体がプログラム固有のため、それはしばらくの間とパターンを記述する、呼び出し元に残っている SleepConditionVariableSRW 関数の単一呼び出しされている体のループ。 この関数はアトミック、条件に一致したウェイク アップを待っている間、SRW ロックを解放します。 代わりには、クリティカル セクションのロックは条件変数を使用する場合も、対応する SleepConditionVariableCS 関数です。

WakeConditionVariable 関数は、単一の待機、モーニング コール、または寝て、スレッドに呼び出されます。 目が覚めるスレッドを返す前に、ロックを再取得します。 また、すべての待機中のスレッドをウェイク アップする WakeAllConditionVariable 関数を使用できます。 図 5が必要 while ループと単純なラッパーを提供します。 睡眠のスレッドが予期せず、目が覚めることが可能ですし、while ループのメモは、スレッドがロックを再取得した後、条件を常に再確認ですします。 また、述語は常にロックを保持している間評価されますに注意してくださいすることが重要です。

図 5 条件変数

class condition_variable
{
  CONDITION_VARIABLE h;
  condition_variable(condition_variable const &);
  condition_variable const & operator=(condition_variable const &);
public:
  condition_variable()
  {
    InitializeConditionVariable(&h);
  }
  template <typename T>
  void wait_while(lock & x, T predicate)
  {
    while (predicate())
    {
      VERIFY(SleepConditionVariableSRW(&h, x.handle(), INFINITE, 0));
    }
  }
  void wake_one()
  {
    WakeConditionVariable(&h);
  }
  void wake_all()
  {
    WakeAllConditionVariable(&h);
  }
};

キューをブロック

このいくつかの形状を与えるためには、私は例として、ブロッキング キューを使用します。 私は一般的にキューをブロック推薦しないことを強調しましょう。 あなたはより良い前者は、以上の同時実行ランタイムの concurrent_queue クラスも抽象化だけである、Windows スレッド プールまたは I/O 完了ポートを使用して提供される可能性があります。 何か非ブロッキングは、一般に好ましいです。 それでも、ブロッキング キューを把握するは簡単な概念と何か多くの開発者は、役に立つようです。 確かに、すべてのプログラムをスケールするには、必要しますが、すべてのプログラムが正しいする必要があります。 ブロッキング キューも同期の正確性を採用する十分な機会を提供しています — そして、それは間違って取得するには、もちろん、十分な機会。

ブロッキング キューだけのロックとイベントの実装を検討します。 共有キュー ロックを保護し、プロデューサーに何かをキューにプッシュしているコンシューマーにイベントを通知します。 図 6 自動リセット イベントを使用して、簡単な例を提供します。 私は push メソッドは、単一の要素だけをキュー、したがって、私は 1 つのコンシューマー キュー オフをポップする目が覚めるしたいので、このイベントのモードを使用します。 Push メソッドは、ロックを取得、キュー要素、任意の待機をコンシューマーにイベントを通知します。 Pop メソッドは、ロックを取得し、キュー要素の取り出しと、それを返す前に空ではないまで待ちます。 両方のメソッドは、lock_block クラスを使用します。 簡潔にするためそれは含まれていないが、それは単に、ロック呼び出しメソッド、コンス トラクターとデストラクターの終了方法を入力します。

図 6 自動リセット ブロック キュー

template <typename T>
class blocking_queue
{
  std::deque<T> q;
  lock x;
  event e;
  blocking_queue(blocking_queue const &);
  blocking_queue const & operator=(blocking_queue const &);
public:
  blocking_queue()
  {
  }
  void push(T const & value)
  {
    lock_block block(x);
    q.push_back(value);
    e.set();
  }
  T pop()
  {
    lock_block block(x);
    while (q.empty())
    {
      x.exit(); e.wait(); // Bug!
x.enter();
    }
    T v = q.front();
    q.pop_front();
    return v;
  }
};

原子出口および待機の呼び出しではないためただし、可能性がデッドロックに注意してください。 ミュー テックス ロックをした場合、SignalObjectAndWait 関数を使用することができるが、ブロッキング キューのパフォーマンスが苦しむことになります。

別のオプションは、手動リセット イベントを使用することです。 要素をキューにたびに信号ではなく、単に 2 つの状態を定義します。 ある限り、キューで、非シグナル要素空のときにイベントを通知することができます。 イベント信号に少ない呼び出しにカーネルがあるのでこれもより多くを実行します。 図 7 この例を提供します。 通知キューに 1 つの要素がある場合、どのように push メソッド イベントを設定します。 SetEvent 関数の不必要な呼び出しを回避できます。 場合は、空のキューを見つける pop メソッドは律儀イベントをクリアします。 複数のキューに置かれた要素がある限り、任意の数の消費者のため、スケーラビリティを向上、イベント オブジェクトに関連のない要素をキューを開くことができます。

図 7 手動リセット ブロック キュー

template <typename T>
class blocking_queue
{
  std::deque<T> q;
  lock x;
  event e;
  blocking_queue(blocking_queue const &);
  blocking_queue const & operator=(blocking_queue const &);
public:
  blocking_queue() :
    e(true) // manual
  {
  }
  void push(T const & value)
  {
    lock_block block(x);
    q.push_back(value);
    if (1 == q.size())
    {
      e.set();
    }
  }
  T pop()
  {
    lock_block block(x);
    while (q.empty())
    {
      x.exit();
      e.wait();
      x.enter();
    }
    T v = q.front();
    q.pop_front();
    if (q.empty())
    {
      e.clear();
    }
    return v;
  }
};

この場合がない潜在的なデッドロック終了待機を入力シーケンスに別の消費者、イベントを盗むことができないので手動リセット イベント与えられて。 パフォーマンスの面でビートは難しい。 それにもかかわらず、代替 (とおそらくより自然な) 解決策はイベントの代わりに条件変数を使用します。 簡単に condition_variable クラスでことができます図 5 ともう少し簡単ですが、マニュアル リセット ブロッキング キューに似ています。 図 8 に例を示します。 セマンティクスと同時実行の意図が上位レベルの同期オブジェクトが採用されより明確になる方法に注意してください。 この明快さは、しばしばペストよりあいまいなコード同時実行のバグを避けるために役立ちます。

図 8 条件変数でブロック キュー

template <typename T>
class blocking_queue
{
  std::deque<T> q;
  lock x;
  condition_variable cv;
  blocking_queue(blocking_queue const &);
  blocking_queue const & operator=(blocking_queue const &);
public:
  blocking_queue()
  {
  }
  void push(T const & value)
  {
    lock_block block(x);
    q.push_back(value);
    cv.wake_one();
  }
  T pop()
  {
    lock_block block(x);
    cv.wait_while(x, [&]()
    {
      return q.empty();
    });
    T v = q.front();
    q.pop_front();
    return v;
  }
};

最後に、私が言及すべきは、C + + 11 今は condition_variable と同様に、ミュー テックスと呼ばれるロックを提供します。 C + + 11 ミュー テックス Windows ミュー テックスとは何の関係もしています。 同様に、C + + 11 condition_variable Windows 条件変数に基づいていません。 これは、移植性の面で良いニュースです。 使用できるどこでも準拠の C++ コンパイラを見つけることができます。 一方、C + + 11 実装で Visual の C++ 2012 のリリースは非常に悪いと比較して、Windows SRW ロックと条件変数を実行します。 図 9 標準の C で実装したブロッキング キューの例を提供します + + 11 ライブラリの型。

図 9 + + 11 ブロック キュー

template <typename T>
class blocking_queue
{
  std::deque<T> q;
  std::mutex x;
  std::condition_variable cv;
  blocking_queue(blocking_queue const &);
  blocking_queue const & operator=(blocking_queue const &);
public:
  blocking_queue()
  {
  }
  void push(T const & value)
  {
    std::lock_guard<std::mutex> lock(x);
    q.push_back(value);
    cv.
notify_one();
  }
  T pop()
  {
    std::unique_lock<std::mutex> lock(x);
    cv.wait(lock, [&]()
    {
      return !q.empty();
    });
    T v = q.front();
    q.pop_front();
    return v;
  }
};

同時実行ライブラリのサポートを一般的には、標準 C++ ライブラリの実装は間違いなく時間の向上させます。 C++ 委員会認識必要があります同時実行のサポートに向けて小さな、保守的な対策を取っているが、まだ作業はしません。 私は私の最後の 3 つの列で説明したように、C++ の同時実行の未来はまだ疑問であります。 今のところ、いくつかの優秀な同期プリミティブ Windows および最新の C++ コンパイラの組み合わせは、軽量でスケーラブルな同時実行制御-セーフ番組の制作のため魅力的なツールキットのためなります。

Kenny Kerr ソフトウェア職人のネイティブ Windows 開発への情熱です。 彼に到達 kennykerr.ca

この記事のレビュー、次技術専門家のおかげで:モハメドイブラヒム Ameen