Guide pratique pour utiliser le verrouillage SpinLock pour une synchronisation de bas niveauHow to: use SpinLock for low-level synchronization

L'exemple suivant montre comment utiliser un verrouillage SpinLock.The following example demonstrates how to use a SpinLock. Dans cet exemple, la section critique exécute une quantité minimale de travail, ce qui en fait un bon candidat pour un SpinLock.In this example, the critical section performs a minimal amount of work, which makes it a good candidate for a SpinLock. Le fait d’augmenter un petit peu le volume de travail augmente le niveau de performance du SpinLock par rapport à un verrouillage standard.Increasing the work a small amount increases the performance of the SpinLock compared to a standard lock. Toutefois, à un moment, le verrouillage tournant devient plus coûteux qu’un verrouillage standard.However, there is a point at which a SpinLock becomes more expensive than a standard lock. Vous pouvez utiliser la fonctionnalité de profilage d'accès concurrentiel dans les outils de profilage pour voir quel type de verrouillage offre les meilleures performances pour votre programme.You can use the concurrency profiling functionality in the profiling tools to see which type of lock provides better performance in your program. Pour plus d’informations, consultez Visualiseur concurrentiel.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

SpinLock peut être utile quand vous avez besoin d’un verrouillage de courte durée sur une ressource partagée.SpinLock might be useful when a lock on a shared resource is not going to be held for very long. Dans ce cas, sur les ordinateurs multicœurs, le thread bloqué peut efficacement tourner pendant quelques cycles jusqu'à ce que le verrouillage soit libéré.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. En tournant, le thread ne se bloque pas. Ce processus est exigeant en ressources.By spinning, the thread does not become blocked, which is a CPU-intensive process. SpinLock cesse de tourner sous certaines conditions pour empêcher toute défaillance des processeurs logiques ou toute inversion des priorités sur les systèmes avec Hyper-Threading.SpinLock will stop spinning under certain conditions to prevent starvation of logical processors or priority inversion on systems with Hyper-Threading.

Cet exemple utilise la classe System.Collections.Generic.Queue<T>, qui exige la synchronisation utilisateur pour l’accès multithread.This example uses the System.Collections.Generic.Queue<T> class, which requires user synchronization for multi-threaded access. Dans les applications qui ciblent .NET Framework version 4, il est également possible d’utiliser le verrouillage System.Collections.Concurrent.ConcurrentQueue<T>, qui ne nécessite aucun verrouillage utilisateur.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.

Notez l’utilisation de false (False en Visual Basic) dans l’appel à SpinLock.Exit.Note the use of false (False in Visual Basic) in the call to SpinLock.Exit. Cela fournit les meilleures performances.This provides the best performance. Dans les architectures IA64, spécifiez true (True dans Visual Basic) pour utiliser la barrière mémoire, qui vide les tampons d’écriture pour garantir que le verrouillage est disponible pour la sortie des autres threads.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.

Voir aussiSee also