Megosztás a következőn keresztül:


A zárolási utasítás – kizárólagos hozzáférés biztosítása megosztott erőforráshoz

Az lock utasítás beszerzi egy adott objektum kölcsönös kizárási zárolását, végrehajt egy utasításblokkot, majd feloldja a zárolást. A zárolás megtartása közben a zárolást tartalmazó szál ismét beszerezheti és feloldhatja a zárolást. Minden más szál le van tiltva a zárolás beszerzésében, és megvárja a zárolás feloldását. Az lock utasítás biztosítja, hogy legfeljebb egy szál hajtja végre a törzsét bármikor.

Az lock utasítás a következő formában történik:

lock (x)
{
    // Your code...
}

A változó x típuskifejezés System.Threading.Lock vagy referenciatípus. Ha x a fordítási időben ismert, hogy a típus System.Threading.Lock, akkor pontosan egyenértékű a következő értékeivel:

using (x.EnterScope())
{
    // Your code...
}

A visszaadott Lock.EnterScope() objektum egy ref struct metódust Dispose() tartalmazó objektum. A létrehozott using utasítás akkor is biztosítja a hatókör felszabadítását, ha kivételt okoz az lock utasítás törzse.

Ellenkező esetben az lock utasítás pontosan egyenértékű a következő értékekkel:

object __lockObj = x;
bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
    // Your code...
}
finally
{
    if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}

Mivel a kód utasítást try-finallyhasznál, a zárolás akkor is ki lesz oldva, ha kivételt ad egy lock utasítás törzsében.

A kifejezés nem használható az await utasítás törzsébenlock.

Irányelvek

A .NET 9-től és a C# 13-tól kezdve a legjobb teljesítmény érdekében zároljon egy ilyen típusú dedikált objektumpéldányt System.Threading.Lock . Emellett a fordító figyelmeztetést ad ki, ha egy ismert Lock objektumot egy másik típusba öntöttek és zároltak. Ha a .NET és a C# régebbi verzióját használja, zároljon egy dedikált objektumpéldányt, amely más célra nem használható. Ne használja ugyanazt a zárolási objektumpéldányt a különböző megosztott erőforrásokhoz, mert az holtpontot vagy zárolási versengést eredményezhet. Különösen kerülje a következő példányok zárolási objektumként való használatát:

  • this, mivel a hívók is zárolhatnak this.
  • Type példányokat, mivel azokat az operátor vagy a tükröződés típusával szerezheti be.
  • sztringpéldányok, beleértve a sztringkonstansokat is, mivel ezek internálhatók.

A zárolási versengés csökkentése érdekében a lehető legrövidebb ideig tartsa lenyomva a zárolást.

Példa

Az alábbi példa egy olyan osztályt Account határoz meg, amely egy dedikált balanceLock példány zárolásával szinkronizálja a privát balance mezőhöz való hozzáférést. Ha ugyanazt a példányt használja a zároláshoz, az biztosítja, hogy két különböző szál ne tudja egyszerre meghívni a balance mezőt vagy Credit metódusokatDebit. A minta a C# 13-at és az új Lock objektumot használja. Ha a C# régebbi verzióját vagy egy régebbi .NET-tárat használ, zárolja a példányt 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# nyelvspecifikáció

További információ: A C# nyelv specifikációjának zárolási utasítás szakasza.

Lásd még