Procedura: utilizzare SpinLock per la sincronizzazione di basso livelloHow to: Use SpinLock for Low-Level Synchronization

L'esempio seguente illustra come usare un oggetto SpinLock.The following example demonstrates how to use a SpinLock.

EsempioExample

In questo esempio la sezione critica esegue una quantità minima di lavoro, quindi è ideale per l'uso di SpinLock.In this example, the critical section performs a minimal amount of work, which makes it a good candidate for a SpinLock. Aumentando leggermente il lavoro, aumentano le prestazioni di SpinLock rispetto a un blocco standard.Increasing the work a small amount increases the performance of the SpinLock compared to a standard lock. Tuttavia, esiste un punto in cui uno SpinLock diventa più costoso di un blocco standard.However, there is a point at which a SpinLock becomes more expensive than a standard lock. È possibile usare la funzionalità del profilo di concorrenza negli strumenti di profilatura per vedere quale tipo di blocco fornisce prestazioni migliori nel programma.You can use the concurrency profiling functionality in the profiling tools to see which type of lock provides better performance in your program. Per altre informazioni, vedere Visualizzatore di concorrenza.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
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

È possibile usare SpinLock quando un blocco su una risorsa condivisa non verrà mantenuto per molto tempo.SpinLock might be useful when a lock on a shared resource is not going to be held for very long. In questi casi, nei computer multicore può essere efficiente per il thread bloccato l'azione di ruotare per alcuni cicli finché il blocco non viene rilasciato.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. Ruotando, il thread non diventa bloccato, che è un processo intensivo della CPU.By spinning, the thread does not become blocked, which is a CPU-intensive process. SpinLock interrompe la rotazione in determinate condizioni per evitare l'esaurimento dei processori logici o l'inversione di priorità nei sistemi con hyperthreading.SpinLock will stop spinning under certain conditions to prevent starvation of logical processors or priority inversion on systems with Hyper-Threading.

Questo esempio usa la classe System.Collections.Generic.Queue<T>, che richiede la sincronizzazione utente per l'accesso a thread multipli.This example uses the System.Collections.Generic.Queue<T> class, which requires user synchronization for multi-threaded access. Nelle applicazioni destinate a .NET Framework versione 4, è anche possibile usare System.Collections.Concurrent.ConcurrentQueue<T>, che non richiede alcun blocco dell'utente.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.

Si noti l'uso di false (False in Visual Basic) nella chiamata a Exit.Note the use of false (False in Visual Basic) in the call to Exit. Questo fornisce prestazioni ottimali.This provides the best performance. Specificare true (True) nelle architetture IA64 per usare il limite di memoria, che scarica il buffer di scrittura per garantire che il blocco sia ora disponibile per l'uscita di altri thread.Specify true (True)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.

Vedere ancheSee Also

Oggetti e funzionalità del threadingThreading Objects and Features