マネージ スレッド プール
更新 : 2010 年 9 月
ThreadPool クラスを使用すると、システムによって管理されるワーカー スレッドのプールがアプリケーションに提供されます。これによって、スレッド管理ではなくアプリケーション タスクに集中できるようになります。 バックグラウンド処理が必要な短いタスクがある場合、マネージ スレッド プールを使用すると、複数のスレッドを簡単に利用できます。 たとえば、.NET Framework Version 4 以降では、スレッド プールのスレッドで非同期タスクを実行する Task オブジェクトと Task<TResult> オブジェクトを作成できます。
メモ |
---|
.NET Framework version 2.0 Service Pack 1 以降では、.NET Framework の以前のリリースでボトルネックとして識別されていた 3 つの重要な部分 (キューへのタスクの配置、スレッド プールのスレッドのディスパッチ、および I/O 完了スレッドのディスパッチ) において、スレッド プールのスループットが大幅に向上しています。この機能を使用するには、アプリケーションで .NET Framework Version 3.5 以降を対象とする必要があります。 |
ユーザー インターフェイスと対話するバックグラウンド タスクの場合、.NET Framework Version 2.0 では、ユーザー インターフェイス スレッドで生成されたイベントを使用して通信する BackgroundWorker クラスも利用できます。
.NET Framework では、非同期 I/O 完了、タイマー コールバック、登録済みの待機操作、デリゲートを使用した非同期メソッド呼び出し、System.Net ソケット接続などのさまざまな目的でスレッド プールのスレッドを使用します。
スレッド プールのスレッドを使用しない場合
次のような場合は、スレッド プールのスレッドを使用する代わりに独自のスレッドを作成して管理する方が適切です。
フォアグラウンド スレッドが必要な場合。
スレッドに特定の優先順位を設定する必要がある場合。
スレッドを長時間にわたってブロックするタスクがある場合。 スレッド プールにはスレッドの最大数が存在するため、多数のスレッド プール スレッドがブロックされると、タスクを開始できなくなることがあります。
スレッドをシングルスレッド アパートメントに配置する必要がある場合。 ThreadPool スレッドはすべてマルチスレッド アパートメントにあります。
スレッドに関連付けられた、確立された ID が必要な場合、またはスレッドを特定のタスク専用にする必要がある場合。
スレッド プールの特徴
スレッド プールのスレッドはバックグラウンド スレッドです。 フォアグラウンド スレッドとバックグラウンド スレッド を参照してください。 各スレッドは、既定のスタック サイズを使用して既定の優先順位で実行します。また、スレッドはマルチスレッド アパートメント内にあります。
各プロセスにはスレッド プールが 1 つだけあります。
スレッド プールのスレッドでの例外
スレッド プールのスレッドで処理できない例外が発生すると、プロセスが終了します。 この規則には、次の 3 つの例外があります。
Abort が呼び出されたため、スレッド プールのスレッドに ThreadAbortException がスローされる。
アプリケーション ドメインがアンロードされるため、スレッド プールのスレッドに AppDomainUnloadedException がスローされる。
共通言語ランタイムまたはホスト プロセスがスレッドを終了する。
詳細については、「マネージ スレッドの例外」を参照してください。
メモ |
---|
.NET Framework Version 1.0 および 1.1 では、スレッド プールのスレッドの未処理の例外は、共通言語ランタイムによって通知なしにトラップされます。このため、アプリケーション状態が破損し、最終的にアプリケーションが停止することにより、デバッグが困難になることがあります。 |
スレッド プールのスレッドの最大数
スレッド プールのキューに配置できる操作の数は、使用できるメモリの容量によってしか制限されませんが、スレッド プールでは、プロセスで同時にアクティブにできるスレッドの数が制限されます。 .NET Framework Version 4 以降では、プロセスのスレッド プールの既定のサイズは、仮想アドレス空間のサイズなど、いくつかの要素によって決まります。 スレッドの数は、プロセスで GetMaxThreads メソッドを呼び出して確認することができます。
スレッドの最大数を制御するには、GetMaxThreads メソッドと SetMaxThreads メソッドを使用します。
メモ |
---|
.NET Framework Version 1.0 および 1.1 では、スレッド プールのサイズはマネージ コードから設定できません。共通言語ランタイムをホストするコードでは、mscoree.h で定義された CorSetMaxThreads を使用してサイズを設定できます。 |
スレッド プールの最小値
スレッド プールでは、カテゴリごとに指定された最小値に達するまで、要求に応じて新しいワーカー スレッドまたは I/O 完了スレッドが作成されます。 これらの最小値は、GetMinThreads メソッドを使用して取得できます。
メモ |
---|
要求が少ないときは、スレッド プールの実際のスレッド数が最小値を下回る場合があります。 |
スレッド プールの最小値に達すると、追加のスレッドが作成されるか、いくつかのタスクが完了するまで待機状態になります。 .NET Framework 4 以降では、スループットを最適化するために、スレッド プールでワーカー スレッドの作成と破棄が行われます。スループットは、タスクの単位時間あたりの完了数として定義されます。 スレッドが少なすぎると使用可能なリソースが最適に使用されない可能性があり、スレッドが多すぎるとリソースの競合が増える可能性があります。
注意 |
---|
アイドル スレッドの最小数は、SetMinThreads メソッドを使用して増やすことができます。ただし、これらの値を必要以上に大きくすると、パフォーマンスの問題が発生する可能性があります。同時に開始するタスクの数が多すぎる場合は、すべてのタスクで処理速度が低下する可能性があります。ほとんどの場合、スレッドを割り当てるための独自のアルゴリズムを使用することでスレッド プールのパフォーマンスが向上します。 |
セキュリティ チェックのスキップ
スレッド プールでは、ThreadPool.UnsafeQueueUserWorkItem メソッドと ThreadPool.UnsafeRegisterWaitForSingleObject メソッドも使用できます。 これらのメソッドは、呼び出し元のスタックが、キューに配置されているタスクの実行時に行われるセキュリティ チェックと無関係であることが確認できる場合にのみ使用します。 QueueUserWorkItem と RegisterWaitForSingleObject はともに呼び出し元のスタックを取り込みます。このスタックは、スレッドがタスクの実行を開始するときにスレッド プールのスレッドのスタックにマージされます。 セキュリティ チェックが必要な場合は、そのスタック全体をチェックする必要があります。 チェックは安全性を提供しますが、パフォーマンスへの影響もあります。
スレッド プールの使用
.NET Framework 4 以降でスレッド プールを使用する場合は、タスク並列ライブラリを使用すると最も簡単です。 Task や Task<TResult> などの並列ライブラリの型では、既定でスレッド プールのスレッドを使用してタスクを実行します。 また、マネージ コードから ThreadPool.QueueUserWorkItem (またはアンマネージ コードから CorQueueUserWorkItem) を呼び出し、タスクを実行するメソッドを表す WaitCallback デリゲートを渡すことによってスレッド プールを使用することもできます。 スレッド プールを使用するもう 1 つの方法として、待機操作の関連ワーク アイテムをキューに置く方法もあります。これは、ThreadPool.RegisterWaitForSingleObject メソッドを使用し、WaitOrTimerCallback デリゲートによって表されるメソッドを呼び出す WaitHandle を、シグナル状態になるときまたはタイムアウトされるときに渡すことによって実行します。 スレッド プールのスレッドを使用してコールバック メソッドが呼び出されます。
ThreadPool の例
このセクションのコード例では、Task クラス、ThreadPool.QueueUserWorkItem メソッド、および ThreadPool.RegisterWaitForSingleObject メソッドを使用したスレッド プールの例を示します。
タスク並列ライブラリを使用した非同期タスクの実行
QueueUserWorkItem を使用した非同期のコード実行
QueueUserWorkItem へのタスク データの供給
RegisterWaitForSingleObject の使用
タスク並列ライブラリを使用した非同期タスクの実行
TaskFactory.StartNew メソッドを呼び出して、Task オブジェクトを作成して使用する方法の例を次に示します。 Task<TResult> クラスを使用して非同期タスクから値を返す方法の例については、「方法: タスクから値を返す」を参照してください。
Imports System.Threading
Imports System.Threading.Tasks
Module StartNewDemo
' Demonstrated features:
' Task ctor()
' Task.Factory
' Task.Wait()
' Task.RunSynchronously()
' Expected results:
' Task t1 (alpha) is created unstarted.
' Task t2 (beta) is created started.
' Task t1's (alpha) start is held until after t2 (beta) is started.
' Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads other than the main thread on multi-core machines.
' Task t3 (gamma) is executed synchronously on the main thread.
' Documentation:
' https://msdn.microsoft.com/en-us/library/system.threading.tasks.task_members(VS.100).aspx
Private Sub Main()
Dim action As Action(Of Object) = Sub(obj As Object)
Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId)
End Sub
' Construct an unstarted task
Dim t1 As New Task(action, "alpha")
' Cosntruct a started task
Dim t2 As Task = Task.Factory.StartNew(action, "beta")
' Block the main thread to demonstate that t2 is executing
t2.Wait()
' Launch t1
t1.Start()
Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId)
' Wait for the task to finish.
' You may optionally provide a timeout interval or a cancellation token
' to mitigate situations when the task takes too long to finish.
t1.Wait()
' Construct an unstarted task
Dim t3 As New Task(action, "gamma")
' Run it synchronously
t3.RunSynchronously()
' Although the task was run synchrounously, it is a good practice to wait for it which observes for
' exceptions potentially thrown by that task.
t3.Wait()
End Sub
End Module
using System;
using System.Threading;
using System.Threading.Tasks;
class StartNewDemo
{
// Demonstrated features:
// Task ctor()
// Task.Factory
// Task.Wait()
// Task.RunSynchronously()
// Expected results:
// Task t1 (alpha) is created unstarted.
// Task t2 (beta) is created started.
// Task t1's (alpha) start is held until after t2 (beta) is started.
// Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads other than the main thread on multi-core machines.
// Task t3 (gamma) is executed synchronously on the main thread.
// Documentation:
// https://msdn.microsoft.com/en-us/library/system.threading.tasks.task_members(VS.100).aspx
static void Main()
{
Action<object> action = (object obj) =>
{
Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
};
// Construct an unstarted task
Task t1 = new Task(action, "alpha");
// Cosntruct a started task
Task t2 = Task.Factory.StartNew(action, "beta");
// Block the main thread to demonstate that t2 is executing
t2.Wait();
// Launch t1
t1.Start();
Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId);
// Wait for the task to finish.
// You may optionally provide a timeout interval or a cancellation token
// to mitigate situations when the task takes too long to finish.
t1.Wait();
// Construct an unstarted task
Task t3 = new Task(action, "gamma");
// Run it synchronously
t3.RunSynchronously();
// Although the task was run synchrounously, it is a good practice to wait for it which observes for
// exceptions potentially thrown by that task.
t3.Wait();
}
}
QueueUserWorkItem を使用した非同期のコード実行
次の例では、ThreadProc メソッドで表される単純なタスクを QueueUserWorkItem メソッドを使用してキューに置きます。
Imports System
Imports System.Threading
Public Class Example
Public Shared Sub Main()
' Queue the task.
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ThreadProc))
Console.WriteLine("Main thread does some work, then sleeps.")
' If you comment out the Sleep, the main thread exits before
' the thread pool task runs. The thread pool uses background
' threads, which do not keep the application running. (This
' is a simple example of a race condition.)
Thread.Sleep(1000)
Console.WriteLine("Main thread exits.")
End Sub
' This thread procedure performs the task.
Shared Sub ThreadProc(stateInfo As Object)
' No state object was passed to QueueUserWorkItem, so
' stateInfo is null.
Console.WriteLine("Hello from the thread pool.")
End Sub
End Class
using System;
using System.Threading;
public class Example
{
public static void Main()
{
// Queue the task.
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
Console.WriteLine("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the thread pool task runs. The thread pool uses background
// threads, which do not keep the application running. (This
// is a simple example of a race condition.)
Thread.Sleep(1000);
Console.WriteLine("Main thread exits.");
}
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo)
{
// No state object was passed to QueueUserWorkItem, so
// stateInfo is null.
Console.WriteLine("Hello from the thread pool.");
}
}
using namespace System;
using namespace System::Threading;
public ref class Example
{
public:
static void Main()
{
// Queue the task.
ThreadPool::QueueUserWorkItem(gcnew WaitCallback(&ThreadProc));
Console::WriteLine("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the thread pool task runs. The thread pool uses background
// threads, which do not keep the application running. (This
// is a simple example of a race condition.)
Thread::Sleep(1000);
Console::WriteLine("Main thread exits.");
}
// This thread procedure performs the task.
static void ThreadProc(Object^ stateInfo)
{
// No state object was passed to QueueUserWorkItem, so
// stateInfo is null.
Console::WriteLine("Hello from the thread pool.");
}
};
int main()
{
Example::Main();
}
QueueUserWorkItem へのタスク データの供給
QueueUserWorkItem を使用してタスクをキューに置き、そのタスクにデータを与えるコード例を次に示します。
Imports System
Imports System.Threading
' TaskInfo holds state information for a task that will be
' executed by a ThreadPool thread.
Public class TaskInfo
' State information for the task. These members
' can be implemented as read-only properties, read/write
' properties with validation, and so on, as required.
Public Boilerplate As String
Public Value As Integer
' Public constructor provides an easy way to supply all
' the information needed for the task.
Public Sub New(text As String, number As Integer)
Boilerplate = text
Value = number
End Sub
End Class
Public Class Example
Public Shared Sub Main()
' Create an object containing the information needed
' for the task.
Dim ti As New TaskInfo("This report displays the number {0}.", 42)
' Queue the task and data.
If ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ThreadProc), ti) Then
Console.WriteLine("Main thread does some work, then sleeps.")
' If you comment out the Sleep, the main thread exits before
' the ThreadPool task has a chance to run. ThreadPool uses
' background threads, which do not keep the application
' running. (This is a simple example of a race condition.)
Thread.Sleep(1000)
Console.WriteLine("Main thread exits.")
Else
Console.WriteLine("Unable to queue ThreadPool request.")
End If
End Sub
' The thread procedure performs the independent task, in this case
' formatting and printing a very simple report.
'
Shared Sub ThreadProc(stateInfo As Object)
Dim ti As TaskInfo = CType(stateInfo, TaskInfo)
Console.WriteLine(ti.Boilerplate, ti.Value)
End Sub
End Class
using System;
using System.Threading;
// TaskInfo holds state information for a task that will be
// executed by a ThreadPool thread.
public class TaskInfo
{
// State information for the task. These members
// can be implemented as read-only properties, read/write
// properties with validation, and so on, as required.
public string Boilerplate;
public int Value;
// Public constructor provides an easy way to supply all
// the information needed for the task.
public TaskInfo(string text, int number)
{
Boilerplate = text;
Value = number;
}
}
public class Example
{
public static void Main()
{
// Create an object containing the information needed
// for the task.
TaskInfo ti = new TaskInfo("This report displays the number {0}.", 42);
// Queue the task and data.
if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti))
{
Console.WriteLine("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the ThreadPool task has a chance to run. ThreadPool uses
// background threads, which do not keep the application
// running. (This is a simple example of a race condition.)
Thread.Sleep(1000);
Console.WriteLine("Main thread exits.");
}
else
{
Console.WriteLine("Unable to queue ThreadPool request.");
}
}
// The thread procedure performs the independent task, in this case
// formatting and printing a very simple report.
//
static void ThreadProc(Object stateInfo)
{
TaskInfo ti = (TaskInfo) stateInfo;
Console.WriteLine(ti.Boilerplate, ti.Value);
}
}
using namespace System;
using namespace System::Threading;
// TaskInfo holds state information for a task that will be
// executed by a ThreadPool thread.
public ref class TaskInfo
{
// State information for the task. These members
// can be implemented as read-only properties, read/write
// properties with validation, and so on, as required.
public:
String^ Boilerplate;
int Value;
// Public constructor provides an easy way to supply all
// the information needed for the task.
TaskInfo(String^ text, int number)
{
Boilerplate = text;
Value = number;
}
};
public ref class Example
{
public:
static void Main()
{
// Create an object containing the information needed
// for the task.
TaskInfo^ ti = gcnew TaskInfo("This report displays the number {0}.", 42);
// Queue the task and data.
if (ThreadPool::QueueUserWorkItem(gcnew WaitCallback(&ThreadProc), ti))
{
Console::WriteLine("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the ThreadPool task has a chance to run. ThreadPool uses
// background threads, which do not keep the application
// running. (This is a simple example of a race condition.)
Thread::Sleep(1000);
Console::WriteLine("Main thread exits.");
}
else
{
Console::WriteLine("Unable to queue ThreadPool request.");
}
}
// The thread procedure performs the independent task, in this case
// formatting and printing a very simple report.
//
static void ThreadProc(Object^ stateInfo)
{
TaskInfo^ ti = (TaskInfo^) stateInfo;
Console::WriteLine(ti->Boilerplate, ti->Value);
}
};
int main()
{
Example::Main();
}
RegisterWaitForSingleObject の使用
スレッド処理の次の機能を説明する例を以下に示します。
ThreadPool スレッドで実行するタスクを、RegisterWaitForSingleObject メソッドを使用してキューに配置します。
AutoResetEvent を使用して、タスクに実行を指示します。 EventWaitHandle、AutoResetEvent、CountdownEvent、および ManualResetEvent を参照してください。
WaitOrTimerCallback デリゲートを使用して、タイムアウトと通知の両方を処理します。
RegisteredWaitHandle を使用して、キューに置かれたタスクをキャンセルします。
Imports System
Imports System.Threading
' TaskInfo contains data that will be passed to the callback
' method.
Public Class TaskInfo
public Handle As RegisteredWaitHandle = Nothing
public OtherInfo As String = "default"
End Class
Public Class Example
Public Shared Sub Main()
' The main thread uses AutoResetEvent to signal the
' registered wait handle, which executes the callback
' method.
Dim ev As New AutoResetEvent(false)
Dim ti As New TaskInfo()
ti.OtherInfo = "First task"
' The TaskInfo for the task includes the registered wait
' handle returned by RegisterWaitForSingleObject. This
' allows the wait to be terminated when the object has
' been signaled once (see WaitProc).
ti.Handle = ThreadPool.RegisterWaitForSingleObject( _
ev, _
New WaitOrTimerCallback(AddressOf WaitProc), _
ti, _
1000, _
false _
)
' The main thread waits about three seconds, to demonstrate
' the time-outs on the queued task, and then signals.
Thread.Sleep(3100)
Console.WriteLine("Main thread signals.")
ev.Set()
' The main thread sleeps, which should give the callback
' method time to execute. If you comment out this line, the
' program usually ends before the ThreadPool thread can execute.
Thread.Sleep(1000)
' If you start a thread yourself, you can wait for it to end
' by calling Thread.Join. This option is not available with
' thread pool threads.
End Sub
' The callback method executes when the registered wait times out,
' or when the WaitHandle (in this case AutoResetEvent) is signaled.
' WaitProc unregisters the WaitHandle the first time the event is
' signaled.
Public Shared Sub WaitProc(state As Object, timedOut As Boolean)
' The state object must be cast to the correct type, because the
' signature of the WaitOrTimerCallback delegate specifies type
' Object.
Dim ti As TaskInfo = CType(state, TaskInfo)
Dim cause As String = "TIMED OUT"
If Not timedOut Then
cause = "SIGNALED"
' If the callback method executes because the WaitHandle is
' signaled, stop future execution of the callback method
' by unregistering the WaitHandle.
If Not ti.Handle Is Nothing Then
ti.Handle.Unregister(Nothing)
End If
End If
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.", _
ti.OtherInfo, _
Thread.CurrentThread.GetHashCode().ToString(), _
cause _
)
End Sub
End Class
using System;
using System.Threading;
// TaskInfo contains data that will be passed to the callback
// method.
public class TaskInfo
{
public RegisteredWaitHandle Handle = null;
public string OtherInfo = "default";
}
public class Example
{
public static void Main(string[] args)
{
// The main thread uses AutoResetEvent to signal the
// registered wait handle, which executes the callback
// method.
AutoResetEvent ev = new AutoResetEvent(false);
TaskInfo ti = new TaskInfo();
ti.OtherInfo = "First task";
// The TaskInfo for the task includes the registered wait
// handle returned by RegisterWaitForSingleObject. This
// allows the wait to be terminated when the object has
// been signaled once (see WaitProc).
ti.Handle = ThreadPool.RegisterWaitForSingleObject(
ev,
new WaitOrTimerCallback(WaitProc),
ti,
1000,
false );
// The main thread waits three seconds, to demonstrate the
// time-outs on the queued thread, and then signals.
Thread.Sleep(3100);
Console.WriteLine("Main thread signals.");
ev.Set();
// The main thread sleeps, which should give the callback
// method time to execute. If you comment out this line, the
// program usually ends before the ThreadPool thread can execute.
Thread.Sleep(1000);
// If you start a thread yourself, you can wait for it to end
// by calling Thread.Join. This option is not available with
// thread pool threads.
}
// The callback method executes when the registered wait times out,
// or when the WaitHandle (in this case AutoResetEvent) is signaled.
// WaitProc unregisters the WaitHandle the first time the event is
// signaled.
public static void WaitProc(object state, bool timedOut)
{
// The state object must be cast to the correct type, because the
// signature of the WaitOrTimerCallback delegate specifies type
// Object.
TaskInfo ti = (TaskInfo) state;
string cause = "TIMED OUT";
if (!timedOut)
{
cause = "SIGNALED";
// If the callback method executes because the WaitHandle is
// signaled, stop future execution of the callback method
// by unregistering the WaitHandle.
if (ti.Handle != null)
ti.Handle.Unregister(null);
}
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
ti.OtherInfo,
Thread.CurrentThread.GetHashCode().ToString(),
cause
);
}
}
using namespace System;
using namespace System::Threading;
// TaskInfo contains data that will be passed to the callback
// method.
public ref class TaskInfo
{
public:
static RegisteredWaitHandle^ Handle = nullptr;
static String^ OtherInfo = "default";
};
public ref class Example
{
public:
static void Main()
{
// The main thread uses AutoResetEvent to signal the
// registered wait handle, which executes the callback
// method.
AutoResetEvent^ ev = gcnew AutoResetEvent(false);
TaskInfo^ ti = gcnew TaskInfo();
ti->OtherInfo = "First task";
// The TaskInfo for the task includes the registered wait
// handle returned by RegisterWaitForSingleObject. This
// allows the wait to be terminated when the object has
// been signaled once (see WaitProc).
ti->Handle = ThreadPool::RegisterWaitForSingleObject(
ev,
gcnew WaitOrTimerCallback(&WaitProc),
ti,
1000,
false );
// The main thread waits three seconds, to demonstrate the
// time-outs on the queued thread, and then signals.
Thread::Sleep(3100);
Console::WriteLine("Main thread signals.");
ev->Set();
// The main thread sleeps, which should give the callback
// method time to execute. If you comment out this line, the
// program usually ends before the ThreadPool thread can execute.
Thread::Sleep(1000);
// If you start a thread yourself, you can wait for it to end
// by calling Thread.Join. This option is not available with
// thread pool threads.
}
// The callback method executes when the registered wait times out,
// or when the WaitHandle (in this case AutoResetEvent) is signaled.
// WaitProc unregisters the WaitHandle the first time the event is
// signaled.
static void WaitProc(Object^ state, bool timedOut)
{
// The state object must be cast to the correct type, because the
// signature of the WaitOrTimerCallback delegate specifies type
// Object.
TaskInfo^ ti = (TaskInfo^) state;
String^ cause = "TIMED OUT";
if (!timedOut)
{
cause = "SIGNALED";
// If the callback method executes because the WaitHandle is
// signaled, stop future execution of the callback method
// by unregistering the WaitHandle.
if (ti->Handle != nullptr)
ti->Handle->Unregister(nullptr);
}
Console::WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
ti->OtherInfo,
Thread::CurrentThread->GetHashCode().ToString(),
cause
);
}
};
int main()
{
Example::Main();
}
参照
処理手順
参照
概念
その他の技術情報
履歴の変更
日付 |
履歴 |
理由 |
---|---|---|
2010 年 9 月 |
既定のサイズと新しいスレッドの作成に関する古い情報を修正。 タスク並列ライブラリの例を追加。 |
コンテンツ バグ修正 |