Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die lock Anweisung erhält die gegenseitigen Ausschlusssperre für ein bestimmtes Objekt, führt einen Anweisungsblock aus und gibt dann die Sperre frei. Während eine Sperre gehalten wird, kann der Thread, der die Sperre enthält, die Sperre erneut abrufen und freigeben. Jeder andere Thread wird daran gehindert, die Sperre zu erhalten und wartet, bis die Sperre losgelassen wird. Die lock Anweisung stellt sicher, dass jeweils maximal ein Thread seinen Textkörper ausführt.
Die lock Anweisung hat die folgende Form:
lock (x)
{
// Your code...
}
Die Variable x ist ein Ausdruck vom System.Threading.Lock Typ oder ein Verweistyp. Wenn x zur Kompilierungszeit als Typ System.Threading.Lockbekannt ist, entspricht es genau folgendem:
using (x.EnterScope())
{
// Your code...
}
Das zurückgegebene Lock.EnterScope() Objekt ist ein ref struct Objekt, das eine Dispose() Methode enthält. Die generierte using Anweisung stellt sicher, dass der Bereich freigegeben wird, auch wenn eine Ausnahme mit dem Textkörper der lock Anweisung ausgelöst wird.
Andernfalls entspricht die lock Anweisung genau folgendem:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Da der Code eine try-finally Anweisung verwendet, wird die Sperre freigegeben, auch wenn eine Ausnahme innerhalb des Textkörpers einer lock Anweisung ausgelöst wird.
Sie können den await Ausdruck nicht im Textkörper einer lock Anweisung verwenden.
Leitlinien
Ab .NET 9 und C# 13 sperren Sie eine dedizierte Objektinstanz des System.Threading.Lock Typs, um optimale Leistung zu erzielen. Darüber hinaus gibt der Compiler eine Warnung aus, wenn ein bekanntes Lock Objekt in einen anderen Typ umgeformt und gesperrt wird. Wenn Sie eine ältere Version von .NET und C# verwenden, sperren Sie eine dedizierte Objektinstanz, die nicht für einen anderen Zweck verwendet wird. Vermeiden Sie die Verwendung derselben Sperrobjektinstanz für verschiedene freigegebene Ressourcen, da dies zu Deadlock- oder Sperrkonflikten führen kann. Vermeiden Sie insbesondere die Verwendung der folgenden Instanzen als Sperrobjekte:
-
this, da Anrufer möglicherweise auch sperrenthis. - Type Instanzen, wie sie durch den Typofoperator oder die Spiegelung abgerufen werden können.
- Zeichenfolgeninstanzen, einschließlich Zeichenfolgenliteralen, wie sie möglicherweise interniert werden.
Halten Sie eine Sperre so kurz wie möglich gedrückt, um die Sperrverknügung zu reduzieren.
Beispiel
Im folgenden Beispiel wird eine Account Klasse definiert, die den Zugriff auf das private balance Feld synchronisiert, indem sie eine dedizierte balanceLock Instanz sperren. Wenn Sie dieselbe Instanz zum Sperren verwenden, wird sichergestellt, dass zwei verschiedene Threads das balance Feld nicht aktualisieren können, indem die Debit Methoden gleichzeitig aufgerufen werden Credit . Im Beispiel wird C# 13 und das neue Lock Objekt verwendet. Wenn Sie eine ältere Version von C# oder eine ältere .NET-Bibliothek verwenden, sperren Sie eine Instanz von object.
using System;
using System.Threading.Tasks;
public class Account
{
// Use `object` in versions earlier than C# 13
private readonly System.Threading.Lock _balanceLock = new();
private decimal _balance;
public Account(decimal initialBalance) => _balance = initialBalance;
public decimal Debit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The debit amount cannot be negative.");
}
decimal appliedAmount = 0;
lock (_balanceLock)
{
if (_balance >= amount)
{
_balance -= amount;
appliedAmount = amount;
}
}
return appliedAmount;
}
public void Credit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The credit amount cannot be negative.");
}
lock (_balanceLock)
{
_balance += amount;
}
}
public decimal GetBalance()
{
lock (_balanceLock)
{
return _balance;
}
}
}
class AccountTest
{
static async Task Main()
{
var account = new Account(1000);
var tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => Update(account));
}
await Task.WhenAll(tasks);
Console.WriteLine($"Account's balance is {account.GetBalance()}");
// Output:
// Account's balance is 2000
}
static void Update(Account account)
{
decimal[] amounts = [0, 2, -3, 6, -2, -1, 8, -5, 11, -6];
foreach (var amount in amounts)
{
if (amount >= 0)
{
account.Credit(amount);
}
else
{
account.Debit(Math.Abs(amount));
}
}
}
}
C#-Sprachspezifikation
Weitere Informationen finden Sie im Abschnitt "Lock-Anweisung " der C#-Sprachspezifikation.