次の方法で共有


同期データ構造と Windows API の比較

ここでは、コンカレンシー ランタイムが提供する同期データ構造と Windows API が提供する同期データ構造の動作を比較します。

同時実行ランタイムが提供する同期データ構造は、"協調スレッド処理モデル" に従います。 協調スレッド処理モデルでは、同期プリミティブは処理リソースを他のスレッドに明示的に譲渡します。 これは、制御スケジューラまたはオペレーティング システムが、処理リソースを他のスレッドに転送する "プリエンプティブ スレッド処理モデル" とは異なります。

critical_section

concurrency::critical_section クラスは 1 つのプロセスのスレッドでのみ使用できるため、Windows の CRITICAL_SECTION 構造体に似ています。 Windows API のクリティカル セクションの詳細については、「クリティカル セクション オブジェクト」を参照してください。

reader_writer_lock

concurrency::reader_writer_lock クラスは、Windows のスリム リーダー/ライター (SRW) ロックに似ています。 類似点と相違点を次の表に示します。

機能 reader_writer_lock クラス SRW ロック
再入不可能 はい はい
リーダーをライターに昇格 (アップグレード サポート) いいえ いいえ
ライターをリーダーに降格 (ダウングレード サポート) いいえ いいえ
書き込み優先ロック はい いいえ
ライターへの FIFO アクセス はい いいえ

SRW ロックの詳細については、プラットフォーム SDK の「スリム リーダー/ライター (SRW) ロック」を参照してください。

イベント

concurrency::event クラスは、名前のない Windows 手動リセット イベントに似ています。 ただし、event オブジェクトは協調して動作しますが、Windows イベントはプリエンプティブに動作します。 Windows イベントの詳細については、イベント オブジェクトに関する記事を参照してください。

説明

event クラスと Windows イベントの相違点をより詳しく理解するために、次の例について考えてみましょう。 この例により、スケジューラは最大で 2 つの同時タスクを作成した後、event クラスと Windows 手動リセット イベントを使用する 2 つの類似した関数を呼び出すことができます。 各関数は、まず共有イベントがシグナル状態になるのを待機する複数のタスクを作成します。 次に、実行中のタスクに制御を渡し、イベントに通知します。 その後、各関数はシグナル状態イベントを待機します。

コード

// event-comparison.cpp
// compile with: /EHsc
#include <windows.h>
#include <concrtrm.h>
#include <ppl.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Demonstrates the usage of cooperative events.
void RunCooperativeEvents()
{
   // An event object.
   event e;

   // Create a task group and execute five tasks that wait for
   // the event to be set.
   task_group tasks;
   for (int i = 0; i < 5; ++i)
   {
      tasks.run([&] {
         // Print a message before waiting on the event.
         wstringstream ss;
         ss << L"\t\tContext " << GetExecutionContextId() 
            << L": waiting on an event." << endl; 
         wcout << ss.str();

         // Wait for the event to be set.
         e.wait();

         // Print a message after the event is set.
         ss = wstringstream();
         ss << L"\t\tContext " << GetExecutionContextId() 
            << L": received the event." << endl; 
         wcout << ss.str();
      });
   }

   // Wait a sufficient amount of time for all tasks to enter 
   // the waiting state.
   Sleep(1000L);

   // Set the event.

   wstringstream ss;
   ss << L"\tSetting the event." << endl; 
   wcout << ss.str();

   e.set();

   // Wait for all tasks to complete.
   tasks.wait();
}

// Demonstrates the usage of preemptive events.
void RunWindowsEvents()
{
   // A Windows event object.
   HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("Windows Event"));

   // Create a task group and execute five tasks that wait for
   // the event to be set.
   task_group tasks;
   for (int i = 0; i < 5; ++i)
   {
      tasks.run([&] {
         // Print a message before waiting on the event.
         wstringstream ss;
         ss << L"\t\tContext " << GetExecutionContextId() 
            << L": waiting on an event." << endl; 
         wcout << ss.str();

         // Wait for the event to be set.
         WaitForSingleObject(hEvent, INFINITE);

         // Print a message after the event is set.
         ss = wstringstream();
         ss << L"\t\tContext " << GetExecutionContextId() 
            << L": received the event." << endl; 
         wcout << ss.str();
      });
   }

   // Wait a sufficient amount of time for all tasks to enter 
   // the waiting state.
   Sleep(1000L);

   // Set the event.

   wstringstream ss;
   ss << L"\tSetting the event." << endl; 
   wcout << ss.str();

   SetEvent(hEvent);

   // Wait for all tasks to complete.
   tasks.wait();

   // Close the event handle.
   CloseHandle(hEvent);
}

int wmain()
{
   // Create a scheduler policy that allows up to two 
   // simultaneous tasks.
   SchedulerPolicy policy(1, MaxConcurrency, 2);

   // Attach the policy to the current scheduler.
   CurrentScheduler::Create(policy);
   
   wcout << L"Cooperative event:" << endl;
   RunCooperativeEvents();

   wcout << L"Windows event:" << endl;
   RunWindowsEvents();
}

コメント

この例では、次のサンプル出力が生成されます。

Cooperative event:
    Context 0: waiting on an event.
    Context 1: waiting on an event.
    Context 2: waiting on an event.
    Context 3: waiting on an event.
    Context 4: waiting on an event.
    Setting the event.
    Context 5: received the event.
    Context 6: received the event.
    Context 7: received the event.
    Context 8: received the event.
    Context 9: received the event.
Windows event:
    Context 10: waiting on an event.
    Context 11: waiting on an event.
    Setting the event.
    Context 12: received the event.
    Context 14: waiting on an event.
    Context 15: received the event.
    Context 16: waiting on an event.
    Context 17: received the event.
    Context 18: waiting on an event.
    Context 19: received the event.
    Context 13: received the event.

event クラスは協調して動作するため、イベントがシグナル状態になるのを待機している場合、スケジューラは処理リソースを別のコンテキストに再割り当てすることができます。 したがって、event クラスを使用するバージョンで、より多くの作業が実行されます。 Windows イベントを使用するバージョンでは、待機中の各タスクは次のタスクが開始される前にシグナル状態になる必要があります。

タスクの詳細については、「タスクの並列化」を参照してください。

関連項目

同期データ構造