SpinLock 構造体
定義
重要
一部の情報は、リリース前に大きく変更される可能性があるプレリリースされた製品に関するものです。 Microsoft は、ここに記載されている情報について、明示または黙示を問わず、一切保証しません。
ロックが使用可能になるまで繰り返しチェックするループ内で、ロックを取得しようとするスレッドが待機する相互排他ロック プリミティブを提供します。
public value class SpinLock
public struct SpinLock
[System.Runtime.InteropServices.ComVisible(false)]
public struct SpinLock
type SpinLock = struct
[<System.Runtime.InteropServices.ComVisible(false)>]
type SpinLock = struct
Public Structure SpinLock
- 継承
- 属性
例
次の例は、次のコードを使用する方法を SpinLock示しています。
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class SpinLockDemo
{
// Demonstrates:
// Default SpinLock construction ()
// SpinLock.Enter(ref bool)
// SpinLock.Exit()
static void SpinLockSample1()
{
SpinLock sl = new SpinLock();
StringBuilder sb = new StringBuilder();
// Action taken by each parallel job.
// Append to the StringBuilder 10000 times, protecting
// access to sb with a SpinLock.
Action action = () =>
{
bool gotLock = false;
for (int i = 0; i < 10000; i++)
{
gotLock = false;
try
{
sl.Enter(ref gotLock);
sb.Append((i % 10).ToString());
}
finally
{
// Only give up the lock if you actually acquired it
if (gotLock) sl.Exit();
}
}
};
// Invoke 3 concurrent instances of the action above
Parallel.Invoke(action, action, action);
// Check/Show the results
Console.WriteLine("sb.Length = {0} (should be 30000)", sb.Length);
Console.WriteLine("number of occurrences of '5' in sb: {0} (should be 3000)",
sb.ToString().Where(c => (c == '5')).Count());
}
// Demonstrates:
// Default SpinLock constructor (tracking thread owner)
// SpinLock.Enter(ref bool)
// SpinLock.Exit() throwing exception
// SpinLock.IsHeld
// SpinLock.IsHeldByCurrentThread
// SpinLock.IsThreadOwnerTrackingEnabled
static void SpinLockSample2()
{
// Instantiate a SpinLock
SpinLock sl = new SpinLock();
// These MRESs help to sequence the two jobs below
ManualResetEventSlim mre1 = new ManualResetEventSlim(false);
ManualResetEventSlim mre2 = new ManualResetEventSlim(false);
bool lockTaken = false;
Task taskA = Task.Factory.StartNew(() =>
{
try
{
sl.Enter(ref lockTaken);
Console.WriteLine("Task A: entered SpinLock");
mre1.Set(); // Signal Task B to commence with its logic
// Wait for Task B to complete its logic
// (Normally, you would not want to perform such a potentially
// heavyweight operation while holding a SpinLock, but we do it
// here to more effectively show off SpinLock properties in
// taskB.)
mre2.Wait();
}
finally
{
if (lockTaken) sl.Exit();
}
});
Task taskB = Task.Factory.StartNew(() =>
{
mre1.Wait(); // wait for Task A to signal me
Console.WriteLine("Task B: sl.IsHeld = {0} (should be true)", sl.IsHeld);
Console.WriteLine("Task B: sl.IsHeldByCurrentThread = {0} (should be false)", sl.IsHeldByCurrentThread);
Console.WriteLine("Task B: sl.IsThreadOwnerTrackingEnabled = {0} (should be true)", sl.IsThreadOwnerTrackingEnabled);
try
{
sl.Exit();
Console.WriteLine("Task B: Released sl, should not have been able to!");
}
catch (Exception e)
{
Console.WriteLine("Task B: sl.Exit resulted in exception, as expected: {0}", e.Message);
}
mre2.Set(); // Signal Task A to exit the SpinLock
});
// Wait for task completion and clean up
Task.WaitAll(taskA, taskB);
mre1.Dispose();
mre2.Dispose();
}
// Demonstrates:
// SpinLock constructor(false) -- thread ownership not tracked
static void SpinLockSample3()
{
// Create SpinLock that does not track ownership/threadIDs
SpinLock sl = new SpinLock(false);
// Used to synchronize with the Task below
ManualResetEventSlim mres = new ManualResetEventSlim(false);
// We will verify that the Task below runs on a separate thread
Console.WriteLine("main thread id = {0}", Thread.CurrentThread.ManagedThreadId);
// Now enter the SpinLock. Ordinarily, you would not want to spend so
// much time holding a SpinLock, but we do it here for the purpose of
// demonstrating that a non-ownership-tracking SpinLock can be exited
// by a different thread than that which was used to enter it.
bool lockTaken = false;
sl.Enter(ref lockTaken);
// Create a separate Task from which to Exit() the SpinLock
Task worker = Task.Factory.StartNew(() =>
{
Console.WriteLine("worker task thread id = {0} (should be different than main thread id)",
Thread.CurrentThread.ManagedThreadId);
// Now exit the SpinLock
try
{
sl.Exit();
Console.WriteLine("worker task: successfully exited SpinLock, as expected");
}
catch (Exception e)
{
Console.WriteLine("worker task: unexpected failure in exiting SpinLock: {0}", e.Message);
}
// Notify main thread to continue
mres.Set();
});
// Do this instead of worker.Wait(), because worker.Wait() could inline the worker Task,
// causing it to be run on the same thread. The purpose of this example is to show that
// a different thread can exit the SpinLock created (without thread tracking) on your thread.
mres.Wait();
// now Wait() on worker and clean up
worker.Wait();
mres.Dispose();
}
}
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Module SpinLockDemo
' Demonstrates:
' Default SpinLock construction ()
' SpinLock.Enter(ref bool)
' SpinLock.Exit()
Private Sub SpinLockSample1()
Dim sl As New SpinLock()
Dim sb As New StringBuilder()
' Action taken by each parallel job.
' Append to the StringBuilder 10000 times, protecting
' access to sb with a SpinLock.
Dim action As Action =
Sub()
Dim gotLock As Boolean = False
For i As Integer = 0 To 9999
gotLock = False
Try
sl.Enter(gotLock)
sb.Append((i Mod 10).ToString())
Finally
' Only give up the lock if you actually acquired it
If gotLock Then
sl.[Exit]()
End If
End Try
Next
End Sub
' Invoke 3 concurrent instances of the action above
Parallel.Invoke(action, action, action)
' Check/Show the results
Console.WriteLine("sb.Length = {0} (should be 30000)", sb.Length)
Console.WriteLine("number of occurrences of '5' in sb: {0} (should be 3000)", sb.ToString().Where(Function(c) (c = "5"c)).Count())
End Sub
' Demonstrates:
' Default SpinLock constructor (tracking thread owner)
' SpinLock.Enter(ref bool)
' SpinLock.Exit() throwing exception
' SpinLock.IsHeld
' SpinLock.IsHeldByCurrentThread
' SpinLock.IsThreadOwnerTrackingEnabled
Private Sub SpinLockSample2()
' Instantiate a SpinLock
Dim sl As New SpinLock()
' These MRESs help to sequence the two jobs below
Dim mre1 As New ManualResetEventSlim(False)
Dim mre2 As New ManualResetEventSlim(False)
Dim lockTaken As Boolean = False
Dim taskA As Task = Task.Factory.StartNew(
Sub()
Try
sl.Enter(lockTaken)
Console.WriteLine("Task A: entered SpinLock")
mre1.[Set]()
' Signal Task B to commence with its logic
' Wait for Task B to complete its logic
' (Normally, you would not want to perform such a potentially
' heavyweight operation while holding a SpinLock, but we do it
' here to more effectively show off SpinLock properties in
' taskB.)
mre2.Wait()
Finally
If lockTaken Then
sl.[Exit]()
End If
End Try
End Sub)
Dim taskB As Task = Task.Factory.StartNew(
Sub()
mre1.Wait()
' wait for Task A to signal me
Console.WriteLine("Task B: sl.IsHeld = {0} (should be true)", sl.IsHeld)
Console.WriteLine("Task B: sl.IsHeldByCurrentThread = {0} (should be false)", sl.IsHeldByCurrentThread)
Console.WriteLine("Task B: sl.IsThreadOwnerTrackingEnabled = {0} (should be true)", sl.IsThreadOwnerTrackingEnabled)
Try
sl.[Exit]()
Console.WriteLine("Task B: Released sl, should not have been able to!")
Catch e As Exception
Console.WriteLine("Task B: sl.Exit resulted in exception, as expected: {0}", e.Message)
End Try
' Signal Task A to exit the SpinLock
mre2.[Set]()
End Sub)
' Wait for task completion and clean up
Task.WaitAll(taskA, taskB)
mre1.Dispose()
mre2.Dispose()
End Sub
' Demonstrates:
' SpinLock constructor(false) -- thread ownership not tracked
Private Sub SpinLockSample3()
' Create SpinLock that does not track ownership/threadIDs
Dim sl As New SpinLock(False)
' Used to synchronize with the Task below
Dim mres As New ManualResetEventSlim(False)
' We will verify that the Task below runs on a separate thread
Console.WriteLine("main thread id = {0}", Thread.CurrentThread.ManagedThreadId)
' Now enter the SpinLock. Ordinarily, you would not want to spend so
' much time holding a SpinLock, but we do it here for the purpose of
' demonstrating that a non-ownership-tracking SpinLock can be exited
' by a different thread than that which was used to enter it.
Dim lockTaken As Boolean = False
sl.Enter(lockTaken)
' Create a separate Task
Dim worker As Task = Task.Factory.StartNew(
Sub()
Console.WriteLine("worker task thread id = {0} (should be different than main thread id)", Thread.CurrentThread.ManagedThreadId)
' Now exit the SpinLock
Try
sl.[Exit]()
Console.WriteLine("worker task: successfully exited SpinLock, as expected")
Catch e As Exception
Console.WriteLine("worker task: unexpected failure in exiting SpinLock: {0}", e.Message)
End Try
' Notify main thread to continue
mres.[Set]()
End Sub)
' Do this instead of worker.Wait(), because worker.Wait() could inline the worker Task,
' causing it to be run on the same thread. The purpose of this example is to show that
' a different thread can exit the SpinLock created (without thread tracking) on your thread.
mres.Wait()
' now Wait() on worker and clean up
worker.Wait()
mres.Dispose()
End Sub
End Module
注釈
スピン ロックを使用する方法の例については、「 方法: Low-Level同期に SpinLock を使用する」を参照してください。
スピン ロックはリーフ レベルのロックに使用できます。この場合、オブジェクトの割り当ては、サイズが大 Monitorきいか、ガベージ コレクションの負荷が高すぎるために暗示されます。 スピン ロックはブロックを回避するのに役立ちます。ただし、大量のブロックが予想される場合は、スピンが過剰であるためにスピン ロックを使用しないことをお考えください。 スピンは、ロックが細かく、数が多い場合 (リンク リストのノードごとのロックなど) や、ロックの保持時間が常に非常に短い場合に役立ちます。 一般に、スピン ロックを保持している間は、次のいずれかのアクションを回避する必要があります。
ブロック
それ自体がブロックする可能性のあるものを呼び出す
一度に複数のスピンロックを保持し、
動的にディスパッチされた呼び出し (インターフェイスと仮想) を行う
所有していないコードに静的にディスパッチされた呼び出しを行う、または
メモリの割り当て。
SpinLock は、アプリケーションのパフォーマンスを向上させると判断した後にのみ使用する必要があります。 また、パフォーマンス上の理由から、値型であることに SpinLock 注意することも重要です。 このため、2 つのインスタンス (元のインスタンスとコピー) は互いに完全に独立しているため、誤ってインスタンスをコピー SpinLock しないように注意する必要があります。これにより、アプリケーションの誤った動作が発生する可能性があります。 インスタンスを SpinLock 渡す必要がある場合は、値ではなく参照によって渡す必要があります。
読み取り専用フィールドにインスタンスを格納 SpinLock しないでください。
コンストラクター
SpinLock(Boolean) |
デバッグを向上させるためにスレッド ID を追跡するオプションを使用して、SpinLock 構造体の新しいインスタンスを初期化します。 |
プロパティ
IsHeld |
ロックが現在いずれかのスレッドによって保持されているかどうかを取得します。 |
IsHeldByCurrentThread |
ロックが現在のスレッドによって保持されているかどうかを取得します。 |
IsThreadOwnerTrackingEnabled |
このインスタンスに対してスレッド所有権の追跡が有効になっているかどうかを取得します。 |
メソッド
Enter(Boolean) |
メソッド呼び出し内で例外が発生した場合でも、 |
Exit() |
ロックを解放します。 |
Exit(Boolean) |
ロックを解放します。 |
TryEnter(Boolean) |
メソッド呼び出し内で例外が発生した場合でも、 |
TryEnter(Int32, Boolean) |
メソッド呼び出し内で例外が発生した場合でも、 |
TryEnter(TimeSpan, Boolean) |
メソッド呼び出し内で例外が発生した場合でも、 |
適用対象
スレッド セーフ
すべてのメンバーはスレッド セーフであり、複数の SpinLock スレッドから同時に使用できます。