Procedura: abilitare la modalità di rilevamento thread in SpinLock
System.Threading.SpinLock è un blocco di esclusione reciproca di basso livello che è possibile usare per scenari con tempi di attesa molto brevi. SpinLock non è rientrante. Dopo che un thread entra nel blocco, deve uscirne correttamente prima di potervi accedere di nuovo. In genere, qualsiasi tentativo di entrare di nuovo nel blocco causerebbe un deadlock e il debug dei deadlock può rivelarsi molto difficile. Come ausilio per lo sviluppo, System.Threading.SpinLock supporta una modalità di rilevamento dei thread che provoca la generazione di un'eccezione quando un thread tenta di entrare di nuovo in un blocco che già lo contiene. Questo permette di individuare facilmente il punto in cui il thread non è uscito correttamente dal blocco. È possibile attivare la modalità di rilevamento dei thread usando il costruttore SpinLock, che accetta un parametro di input booleano, e passando un argomento true
. Dopo aver completato le fasi di sviluppo e test, disattivare la modalità di rilevamento dei thread per ottenere prestazioni migliori.
Esempio
L'esempio seguente mostra la modalità di rilevamento dei thread. Le righe per l'uscita corretta dal blocco sono impostate come commento per simulare un errore di codifica che restituisce uno dei risultati seguenti:
Viene generata un'eccezione se l'oggetto SpinLock è stato creato usando un argomento
true
(True
in Visual Basic).Viene generato un deadlock se l'oggetto SpinLock è stato creato usando un argomento
false
(False
in Visual Basic).
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SpinLockDemo
{
// C#
public class SpinLockTest
{
// Specify true to enable thread tracking. This will cause
// exception to be thrown when the first thread attempts to reenter the lock.
// Specify false to cause deadlock due to coding error below.
private static SpinLock _spinLock = new SpinLock(true);
static void Main()
{
Parallel.Invoke(
() => DoWork(),
() => DoWork(),
() => DoWork(),
() => DoWork()
);
Console.WriteLine("Press any key.");
Console.ReadKey();
}
public static void DoWork()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++)
{
bool lockTaken = false;
try
{
_spinLock.Enter(ref lockTaken);
// do work here protected by the lock
Thread.SpinWait(50000);
sb.Append(Thread.CurrentThread.ManagedThreadId);
sb.Append(" Entered-");
}
catch (LockRecursionException ex)
{
Console.WriteLine("Thread {0} attempted to reenter the lock",
Thread.CurrentThread.ManagedThreadId);
throw;
}
finally
{
// INTENTIONAL CODING ERROR TO DEMONSTRATE THREAD TRACKING!
// UNCOMMENT THE LINES FOR CORRECT SPINLOCK BEHAVIOR
// Commenting out these lines causes the same thread
// to attempt to reenter the lock. If the SpinLock was
// created with thread tracking enabled, the exception
// is thrown. Otherwise the spinlock deadlocks.
if (lockTaken)
{
// _spinLock.Exit(false);
// sb.Append("Exited ");
}
}
// Output for diagnostic display.
if(i % 4 != 0)
Console.Write(sb.ToString());
else
Console.WriteLine(sb.ToString());
sb.Clear();
}
}
}
}
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Module Module1
Public Class SpinTest
' True means "enable thread tracking." This will cause an
' exception to be thrown when the first thread attempts to reenter the lock.
' Specify False to cause deadlock due to coding error below.
Private Shared _spinLock = New SpinLock(True)
Public Shared Sub Main()
Parallel.Invoke(
Sub() DoWork(),
Sub() DoWork(),
Sub() DoWork(),
Sub() DoWork()
)
Console.WriteLine("Press any key.")
Console.ReadKey()
End Sub
Public Shared Sub DoWork()
Dim sb = New StringBuilder()
For i As Integer = 1 To 9999
Dim lockTaken As Boolean = False
Try
_spinLock.Enter(lockTaken)
' do work here protected by the lock
Thread.SpinWait(50000)
sb.Append(Thread.CurrentThread.ManagedThreadId)
sb.Append(" Entered-")
Catch ex As LockRecursionException
Console.WriteLine("Thread {0} attempted to reenter the lock",
Thread.CurrentThread.ManagedThreadId)
Throw
Finally
' INTENTIONAL CODING ERROR TO DEMONSTRATE THREAD TRACKING!
' UNCOMMENT THE LINES FOR CORRECT SPINLOCK BEHAVIOR
' Commenting out these lines causes the same thread
' to attempt to reenter the lock. If the SpinLock was
' created with thread tracking enabled, the exception
' is thrown. Otherwise, if the SpinLock was created with a
' parameter of false, and these lines are left commented, the spinlock deadlocks.
If (lockTaken) Then
' _spinLock.Exit()
' sb.Append("Exited ")
End If
End Try
' Output for diagnostic display.
If (i Mod 4 <> 0) Then
Console.Write(sb.ToString())
Else
Console.WriteLine(sb.ToString())
End If
sb.Clear()
Next
End Sub
End Class
End Module