次の方法で共有


スピンウェイト

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

こちらも参照ください