System.Threading.SpinWait は、カーネル イベントに必要なコストの高いコンテキスト スイッチとカーネル遷移を回避するために、低レベルのシナリオで使用できる軽量の同期の種類です。 マルチコア コンピューターでは、リソースが長時間保持されることが想定されていない場合、待機中のスレッドがユーザー モードで数十サイクルまたは数百サイクル回転し、リソースの取得を再試行する方が効率的になる可能性があります。 スピン後にリソースが使用可能な場合は、数千サイクルを節約できます。 リソースがまだ使用できない場合は、数サイクルしか費やしていませんが、カーネルベースの待機に入ることができます。 このスピンと待機の組み合わせは、 2 フェーズの待機操作と呼ばれることもあります。
SpinWait は、 ManualResetEventなどのカーネル イベントをラップする .NET 型と組み合わせて使用するように設計されています。 SpinWait は、1 つのプログラムで基本的なスピン機能に単独で使用することもできます。
SpinWait は単なる空のループだけではありません。 一般的なケースに対して正しいスピン動作を提供するように慎重に実装されており、スピンが十分に長い場合 (カーネル遷移に必要な時間のおおよその長さ) にコンテキスト スイッチが開始されます。 たとえば、単一コア コンピューターでは、 SpinWait はスレッドのタイム スライスをすぐに生成します。これは、スピンによってすべてのスレッドの進行がブロックされるためです。 SpinWait は、マルチコア コンピューターでも、待機中のスレッドによって優先順位の高いスレッドやガベージ コレクターがブロックされないように、このスレッドのタイム スライスを明け渡します。 したがって、2 フェーズ待機操作で SpinWait を使用している場合は、 SpinWait 自体がコンテキスト切り替えを開始する前にカーネル待機を呼び出することをお勧めします。 SpinWait には NextSpinWillYield プロパティが用意されています。このプロパティは、 SpinOnceを呼び出すたびに確認できます。 プロパティから true
が返されたら、独自の Wait 操作を開始します。 例については、「 方法: SpinWait を使用して Two-Phase 待機操作を実装する」を参照してください。
2 段階の待機操作を実行していないが、何らかの条件が満たされるまで回転しているだけの場合は、 SpinWait がコンテキスト スイッチを実行できるようにして、Windows オペレーティング システム環境で適切な市民にすることができます。 次の基本的な例は、ロックフリー スタック内の SpinWait を示しています。 高パフォーマンスでスレッド セーフなスタックが必要な場合は、 System.Collections.Concurrent.ConcurrentStack<T>の使用を検討してください。
public class LockFreeStack<T>
{
private volatile Node m_head;
private class Node { public Node Next; public T Value; }
public void Push(T item)
{
var spin = new SpinWait();
Node node = new Node { Value = item }, head;
while (true)
{
head = m_head;
node.Next = head;
if (Interlocked.CompareExchange(ref m_head, node, head) == head) break;
spin.SpinOnce();
}
}
public bool TryPop(out T result)
{
result = default(T);
var spin = new SpinWait();
Node head;
while (true)
{
head = m_head;
if (head == null) return false;
if (Interlocked.CompareExchange(ref m_head, head.Next, head) == head)
{
result = head.Value;
return true;
}
spin.SpinOnce();
}
}
}
Imports System.Threading
Module SpinWaitDemo
Public Class LockFreeStack(Of T)
Private m_head As Node
Private Class Node
Public [Next] As Node
Public Value As T
End Class
Public Sub Push(ByVal item As T)
Dim spin As New SpinWait()
Dim head As Node, node As New Node With {.Value = item}
While True
Thread.MemoryBarrier()
head = m_head
node.Next = head
If Interlocked.CompareExchange(m_head, node, head) Is head Then Exit While
spin.SpinOnce()
End While
End Sub
Public Function TryPop(ByRef result As T) As Boolean
result = CType(Nothing, T)
Dim spin As New SpinWait()
Dim head As Node
While True
Thread.MemoryBarrier()
head = m_head
If head Is Nothing Then Return False
If Interlocked.CompareExchange(m_head, head.Next, head) Is head Then
result = head.Value
Return True
End If
spin.SpinOnce()
End While
End Function
End Class
End Module
こちらも参照ください
.NET