Share via


方法: 特定のスケジューラ ポリシーを指定する

スケジューラでタスクを管理する場合、スケジューラ ポリシーを使用することで、スケジューラが使用する方法を制御できます。 このトピックでは、スケジューラ ポリシーを使用して、プログレス インジケーターをコンソールに出力するタスクのスレッドの優先度を高くする方法について説明します。

カスタム スケジューラ ポリシーを非同期エージェントと共に使用する例については、「方法: 特定のスケジューラ ポリシーを使用するエージェントを作成する」をご覧ください。

次の例では、2 つのタスクを並列で実行します。 最初のタスクは、n番目のフィボナッチ数を計算します。 2 番目のタスクは、プログレス インジケーターをコンソールに出力します。

最初のタスクは、再帰的な分解を使用してフィナボッチ数を計算します。 つまり、各タスクがサブタスクを再帰的に作成して全体的な結果を計算します。 再帰的な分解を使用するタスクは、利用可能なすべてのリソースを使用するため、他のタスクが処理を続行できなくなる可能性があります。 この例では、プログレス インジケーターを出力するタスクが、コンピューティング リソースに適切なタイミングでアクセスできない場合があります。

進行状況メッセージを出力するタスクがコンピューティング リソースに適切なタイミングでアクセスできるように、この例では、「How to: Manage a Scheduler Instance (方法: スケジューラ インスタンスの管理)」で説明されている手順に従って、カスタム ポリシーを持つスケジューラ インスタンスを作成しています。 このカスタム ポリシーで、スレッドの優先順位が最も高い優先順位クラスとなるように指定します。

この例は、concurrency::call クラスと concurrency::timer クラスを使用してプログレス インジケーターを出力します。 これらのクラスには、そのスケジュールを設定する concurrency::Scheduler オブジェクトへの参照を取得するバージョンのコンストラクターがあります。 この例は、フィボナッチ数を計算するタスクをスケジュールする既定のスケジューラと、プログレス インジケーターを出力するタスクをスケジュールするスケジューラ インスタンスを使用します。

カスタム ポリシーを持つスケジューラを使用する利点を示すため、この例ではタスク全体を 2 度実行しています。 まず、既定のスケジューラを使用して両方のタスクをスケジュールします。 次に、既定のスケジューラを使用して最初のタスクをスケジュールし、カスタム ポリシーを持つスケジューラを使用して 2 番目のタスクをスケジュールします。

// scheduler-policy.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;

   // Compute the components in parallel.
   int n1, n2;
   parallel_invoke(
      [n,&n1] { n1 = fibonacci(n-1); },
      [n,&n2] { n2 = fibonacci(n-2); }
   );
  
   return n1 + n2;
}

// Prints a progress indicator while computing the nth Fibonacci number.
void fibonacci_with_progress(Scheduler& progress_scheduler, int n)
{
   // Use a task group to compute the Fibonacci number.
   // The tasks in this group are scheduled by the current scheduler.
   structured_task_group tasks;

   auto task = make_task([n] {
      fibonacci(n);
   });
   tasks.run(task);

   // Create a call object that prints its input to the console.
   // This example uses the provided scheduler to schedule the 
   // task that the call object performs.
   call<wchar_t> c(progress_scheduler, [](wchar_t c) { 
      wcout << c; 
   });

   // Connect the call object to a timer object. The timer object
   // sends a progress message to the call object every 100 ms.
   // This example also uses the provided scheduler to schedule the 
   // task that the timer object performs.
   timer<wchar_t> t(progress_scheduler, 100, L'.', &c, true);
   t.start();

   // Wait for the task that computes the Fibonacci number to finish.
   tasks.wait();

   // Stop the timer.
   t.stop();

   wcout << L"done" << endl;
}

int wmain()
{  
   // Calculate the 38th Fibonacci number.
   const int n = 38;

   // Use the default scheduler to schedule the progress indicator while 
   // the Fibonacci number is calculated in the background.

   wcout << L"Default scheduler:" << endl;
   fibonacci_with_progress(*CurrentScheduler::Get(), n);

   // Now use a scheduler that has a custom policy for the progress indicator.
   // The custom policy specifies the thread priority to the highest 
   // priority class.
   
   SchedulerPolicy policy(1, ContextPriority, THREAD_PRIORITY_HIGHEST);
   Scheduler* scheduler = Scheduler::Create(policy);

   // Register to be notified when the scheduler shuts down.
   HANDLE hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
   scheduler->RegisterShutdownEvent(hShutdownEvent);

   wcout << L"Scheduler that has a custom policy:" << endl;
   fibonacci_with_progress(*scheduler, n);

   // Release the final reference to the scheduler. This causes the scheduler
   // to shut down.
   scheduler->Release();

   // Wait for the scheduler to shut down and destroy itself.
   WaitForSingleObject(hShutdownEvent, INFINITE);

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

この例を実行すると、次の出力が生成されます。

Default scheduler:
...........................................................................done
Scheduler that has a custom policy:
...........................................................................done

どちらのタスク セットでも結果は同じですが、カスタム ポリシーを使用するバージョンではプログレス インジケーターを出力するタスクを高い優先順位で実行できるため、応答性が向上します。

コードのコンパイル

コード例をコピーし、Visual Studio プロジェクトに貼り付けるか、scheduler-policy.cpp という名前のファイルに貼り付けてから、Visual Studio のコマンド プロンプト ウィンドウで次のコマンドを実行します。

cl.exe /EHsc scheduler-policy.cpp

関連項目

スケジューラ ポリシー
方法: スケジューラ インスタンスを管理する
方法: 特定のスケジューラ ポリシーを使用するエージェントを作成する