Gewusst wie: Aktivieren des Modus zum Nachverfolgen von Threads in SpinLock
System.Threading.SpinLock ist eine gegenseitige Ausschlusssperre auf niedriger Ebene, die Sie für Szenarien verwenden können, die sehr kurze Wartezeiten haben. SpinLock ist nicht eintrittsinvariant. Nachdem ein Thread die Sperre angefordert hat, muss er die Sperre ordnungsgemäß beenden, bevor er sie wieder anfordern kann. In der Regel verursacht jeder Versuch, die Sperre erneut anzufordern, einen Deadlock, und das Debuggen von Deadlocks kann sehr schwierig sein. Als Hilfe bei der Entwicklung unterstützt System.Threading.SpinLock einen Modus zum Nachverfolgen von Threads. In diesem Modus wird eine Ausnahme ausgelöst, wenn ein Thread versucht, eine Sperre, die er bereits besitzt, erneut anzufordern. Auf diese Weise können Sie die Stelle, an der die Sperre nicht ordnungsgemäß beendet wurde, leichter finden. Sie können den Modus zum Nachverfolgen von Threads mit dem SpinLock-Konstruktor aktivieren, der einen booleschen Eingabeparameter akzeptiert, und das Argument true übergeben. Nachdem Sie die Entwicklungs- und Testphase abgeschlossen haben, deaktivieren Sie den Modus zum Nachverfolgen von Threads, um eine bessere Leistung zu erzielen.
Beispiel
Das folgende Beispiel veranschaulicht den Modus zum Nachverfolgen von Threads. Die Zeilen, die die Sperre ordnungsgemäß beenden, sind auskommentiert, um einen Codierungsfehler zu simulieren, der zu einem der folgenden Ergebnisse führt:
Eine Ausnahme wird ausgelöst, wenn SpinLock mit dem Argument true (True in Visual Basic) erstellt wurde.
Ein Deadlock tritt auf, wenn SpinLock mit dem Argument false (False in Visual Basic) erstellt wurde.
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
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();
}
}
}
}