lock-instruktion – säkerställa exklusiv åtkomst till en delad resurs

-instruktionen lock hämtar låset för ömsesidig uteslutning för ett visst objekt, kör ett instruktionsblock och släpper sedan låset. Medan ett lås hålls, kan tråden som innehåller låset igen hämta och frigöra låset. Alla andra trådar blockeras från att hämta låset och väntar tills låset släpps. -instruktionen lock ser till att endast en tråd kör dess brödtext när som helst.

Instruktionen lock är av formuläret

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

där x är ett uttryck av en referenstyp. Det är exakt likvärdigt med

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

Eftersom koden använder en try-finally -instruktion frigörs låset även om ett undantag utlöses i brödtexten i en lock -instruktion.

Du kan inte använda await uttrycket i brödtexten i en lock -instruktion.

Riktlinjer

När du synkroniserar trådåtkomst till en delad resurs låser du på en dedikerad objektinstans (till exempel private readonly object balanceLock = new object();) eller en annan instans som sannolikt inte kommer att användas som ett låsobjekt av orelaterade delar av koden. Undvik att använda samma låsobjektinstans för olika delade resurser, eftersom det kan leda till dödläge eller låskonkurrering. Undvik särskilt att använda följande instanser som låsobjekt:

  • this, eftersom den kan användas av anroparna som ett lås.
  • Type instanser, eftersom de kan hämtas av typ av operator eller reflektion.
  • stränginstanser, inklusive strängliteraler, eftersom de kan vara internerade.

Håll ett lås så kort tid som möjligt för att minska låskonkurrationen.

Exempel

I följande exempel definieras en Account klass som synkroniserar åtkomsten till det privata balance fältet genom att låsa på en dedikerad balanceLock instans. Om du använder samma instans för låsning ser du till att balance fältet inte kan uppdateras samtidigt av två trådar som försöker anropa Debit metoderna eller Credit samtidigt.

using System;
using System.Threading.Tasks;

public class Account
{
    private readonly object balanceLock = new object();
    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));
            }
        }
    }
}

Språkspecifikation för C#

Mer information finns i avsnittet låsuttryck i C#-språkspecifikationen.

Se även