Como usar SpinLock para sincronização de baixo nívelHow to: use SpinLock for low-level synchronization

O exemplo a seguir demonstra como usar o SpinLock.The following example demonstrates how to use a SpinLock. No exemplo, a seção crítica executa uma quantidade mínima de trabalho, o que a torna uma boa candidata para o SpinLock.In this example, the critical section performs a minimal amount of work, which makes it a good candidate for a SpinLock. Aumentar um pouco o trabalho aumenta o desempenho do SpinLock em comparação com um bloqueio padrão.Increasing the work a small amount increases the performance of the SpinLock compared to a standard lock. No entanto, há um ponto em que um SpinLock se torna mais caro do que um bloqueio padrão.However, there is a point at which a SpinLock becomes more expensive than a standard lock. Use a funcionalidade de Criação de Perfil de Simultaneidade nas ferramentas de criação de perfil para ver qual tipo de bloqueio oferece o melhor desempenho no seu programa.You can use the concurrency profiling functionality in the profiling tools to see which type of lock provides better performance in your program. Para saber mais, confira Visualização Simultânea.For more information, see Concurrency Visualizer.


 class SpinLockDemo2
 {        
     const int N = 100000;
     static Queue<Data> _queue = new Queue<Data>();
     static object _lock = new Object();
     static SpinLock _spinlock = new SpinLock();

     class Data
     {
         public string Name { get; set; }
         public double Number { get; set; }
     }
     static void Main(string[] args)
     {

         // First use a standard lock for comparison purposes.
         UseLock();
         _queue.Clear();
         UseSpinLock();            
         
         Console.WriteLine("Press a key");
         Console.ReadKey();
     }

     private static void UpdateWithSpinLock(Data d, int i)
     {             
         bool lockTaken = false;
         try
         {
             _spinlock.Enter(ref lockTaken);
             _queue.Enqueue( d );                
         }
         finally
         { 
             if (lockTaken) _spinlock.Exit(false);
         } 
     }

     private static void UseSpinLock()
     {
           
           Stopwatch sw = Stopwatch.StartNew();            
         
           Parallel.Invoke(
                   () => {
                       for (int i = 0; i < N; i++)
                       {
                           UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
                       }
                   },
                   () => {
                       for (int i = 0; i < N; i++)
                       {
                           UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i);
                       }                          
                   }
               );
           sw.Stop();
           Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds);
     }

     static void UpdateWithLock(Data d, int i)
     {
         lock (_lock)
         {
             _queue.Enqueue(d);
         } 
     }

     private static void UseLock()
     {
         Stopwatch sw = Stopwatch.StartNew();
        
         Parallel.Invoke(
                 () => {
                     for (int i = 0; i < N; i++)
                     {
                         UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
                     }
                 },
                 () => {
                     for (int i = 0; i < N; i++)
                     {
                         UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i);
                     }                        
                 }
             );
         sw.Stop();
         Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds);
     }
 }
Imports System.Threading
Imports System.Threading.Tasks

Class SpinLockDemo2

    Const N As Integer = 100000
    Shared _queue = New Queue(Of Data)()
    Shared _lock = New Object()
    Shared _spinlock = New SpinLock()

    Class Data
        Public Name As String
        Public Number As Double
    End Class
    Shared Sub Main()

        ' First use a standard lock for comparison purposes.
        UseLock()
        _queue.Clear()
        UseSpinLock()

        Console.WriteLine("Press a key")
        Console.ReadKey()

    End Sub

    Private Shared Sub UpdateWithSpinLock(ByVal d As Data, ByVal i As Integer)

        Dim lockTaken As Boolean = False
        Try
            _spinlock.Enter(lockTaken)
            _queue.Enqueue(d)
        Finally

            If lockTaken Then
                _spinlock.Exit(False)
            End If
        End Try
    End Sub

    Private Shared Sub UseSpinLock()


        Dim sw = Stopwatch.StartNew()

        Parallel.Invoke(
               Sub()
                   For i As Integer = 0 To N - 1
                       UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                   Next
               End Sub,
                Sub()
                    For i As Integer = 0 To N - 1
                        UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                    Next
                End Sub
            )
        sw.Stop()
        Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds)
    End Sub

    Shared Sub UpdateWithLock(ByVal d As Data, ByVal i As Integer)

        SyncLock (_lock)
            _queue.Enqueue(d)
        End SyncLock
    End Sub

    Private Shared Sub UseLock()

        Dim sw = Stopwatch.StartNew()

        Parallel.Invoke(
                Sub()
                    For i As Integer = 0 To N - 1
                        UpdateWithLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                    Next
                End Sub,
               Sub()
                   For i As Integer = 0 To N - 1
                       UpdateWithLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                   Next
               End Sub
                )
        sw.Stop()
        Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds)
    End Sub
End Class

O SpinLock poderá ser útil quando um bloqueio em um recurso compartilhado não será mantido por muito tempo.SpinLock might be useful when a lock on a shared resource is not going to be held for very long. Nesses casos, em computadores com vários núcleos, pode ser eficiente para o thread bloqueado girar por alguns ciclos até que o bloqueio seja liberado.In such cases, on multi-core computers it can be efficient for the blocked thread to spin for a few cycles until the lock is released. Ao girar, o thread não fica bloqueado, que é um processo de uso intenso da CPU.By spinning, the thread does not become blocked, which is a CPU-intensive process. O SpinLock cessará de girar sob determinadas condições para evitar a privação dos processadores lógicos ou a inversão de prioridades em sistemas com o Hyper-Threading.SpinLock will stop spinning under certain conditions to prevent starvation of logical processors or priority inversion on systems with Hyper-Threading.

Este exemplo usa a classe System.Collections.Generic.Queue<T>, que requer a sincronização de usuário para o acesso de threads múltiplos.This example uses the System.Collections.Generic.Queue<T> class, which requires user synchronization for multi-threaded access. Outra opção nos aplicativos que direcionam para a versão 4 do .NET Framework é usar o System.Collections.Concurrent.ConcurrentQueue<T>, que não exige bloqueio de usuário.In applications that target the .NET Framework version 4, another option is to use the System.Collections.Concurrent.ConcurrentQueue<T>, which does not require any user locks.

Observe o uso do false (False no Visual Basic) na chamada do SpinLock.Exit.Note the use of false (False in Visual Basic) in the call to SpinLock.Exit. Isso oferece o melhor desempenho.This provides the best performance. Especifique true (True em Visual Basic) em arquiteturas de IA64 para usar o limite de memória, que libera os buffers de gravação para garantir que o bloqueio esteja disponível para outros threads saírem.Specify true (True in Visual Basic) on IA64 architectures to use the memory fence, which flushes the write buffers to ensure that the lock is now available for other threads to exit.

Consulte tambémSee also