Neu in .NET 9.0 [10]: Neue Klasse fĂĽr lock-Statements in C# 13.0

C# 13.0 führt die neue Klasse Lock ein, um Codeblöcke vor dem Zugriff durch weitere Threads zu sperren.

vorlesen Druckansicht 3 Kommentare lesen
StraĂźenschild mit C# drauf.

(Bild: Pincasso/Shutterstock)

Lesezeit: 1 Min.
Von
  • Dr. Holger Schwichtenberg

Ab .NET 9.0/C# 13.0 gibt es für das Sperren von Codeblöcken vor dem Zugriff durch weitere Threads die neue Klasse System.Threading.Lock, die man nun im Standard in Verbindung mit dem lock-Statement in C# verwenden sollte, "for best performance" wie Microsoft in der Dokumentation schreibt.

Der Dotnet-Doktor – Holger Schwichtenberg
Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.

Videos by heise

Folgender Code aus der C#-Dokumentation auf Microsoft Learn zeigt ein Beispiel mit dem SchlĂĽsselwort lock und der Klasse System.Threading.Lock:

using System;
using System.Threading.Tasks;

namespace NET9_Console.CS13;

public class Account
{
 // Vor .NET 9.0/C# 13.0 wurde hier System.Object verwendet statt 
 // System.Threading.Lock 
 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));
   }
  }
 }
}

Der C#-13.0-Compiler generiert dann aus

lock (_balanceLock)
{
  _balance += amount;
}

einen Aufruf der EnterScope()-Methode in der Klasse System.Threading.Lock:

using (balanceLock.EnterScope())
{
  _balance += amount;
}

(rme)