lock ステートメント (C# リファレンス)lock statement (C# Reference)

lock ステートメントは、指定のオブジェクトに対する相互排他ロックを取得し、ステートメント ブロックを実行してからロックを解放します。The lock statement obtains the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. ロックが保持されている間、ロックを保持するスレッドはロックを再度取得し、解放できます。While a lock is held, the thread that holds the lock can again obtain and release the lock. 他のスレッドはブロックされてロックを取得できず、ロックが解放されるまで待機します。Any other thread is blocked from obtaining the lock and waits until the lock is released.

lock ステートメントの形式は次のようになります。The lock statement is of the form

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

x参照型の式です。where x is an expression of a reference type. これは次にまったく等しくなります。It's precisely equivalent to

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

このコードでは try...finally ブロックが使用されているため、lock ステートメントの本文内で例外がスローされた場合でもロックは解放されます。Since the code uses a try...finally block, the lock is released even if an exception is thrown within the body of a lock statement.

lock ステートメントの本体で await キーワードを使用することはできません。You can't use the await keyword in the body of a lock statement.

コメントRemarks

共有リソースへのスレッド アクセスを同期する場合、専用オブジェクト インスタンス (private readonly object balanceLock = new object(); など) またはコードの関連のない部分によってロック オブジェクトとして使用される可能性がない別のインスタンスをロックします。When you synchronize thread access to shared resource, lock on a dedicated object instance (for example, private readonly object balanceLock = new object();) or another instance that is unlikely to be used as a lock object by unrelated parts of the code. 異なる共有リソースに対して同じロック オブジェクト インスタンスを使用することは避けてください。デッドロックやロックの競合が発生する可能性があります。Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. 特に以下の使用は避けてください。In particular, avoid using

  • this (ロックとして呼び出し元に使用される可能性があります)。this (might be used by the callers as a lock),
  • Type インスタンス (typeof 演算子またはリフレクションによって取得される可能性があります)。Type instances (might be obtained by the typeof operator or reflection),
  • ロック オブジェクトとしてのstring instances, including string literals,

文字列インスタンス (文字列リテラルなど)。as lock objects.

Example

次の例では、専用 balanceLock インスタンスをロックすることでそのプライベート balance フィールドへのアクセスを同期する Account クラスが定義されます。The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. ロッキングに同じインスタンスを使用すると、2 つのスレッドが Debit または Credit メソッドを同時に呼び出すことによって balance フィールドを同時に更新することができなくなります。Using the same instance for locking ensures that the balance field cannot be updated simultaneously by two threads attempting to call the Debit or Credit methods simultaneously.

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)
    {
        lock (balanceLock)
        {
            if (balance >= amount)
            {
                Console.WriteLine($"Balance before debit :{balance, 5}");
                Console.WriteLine($"Amount to remove     :{amount, 5}");
                balance = balance - amount;
                Console.WriteLine($"Balance after debit  :{balance, 5}");
                return amount;
            }
            else
            {
                return 0;
            }
        }
    }

    public void Credit(decimal amount)
    {
        lock (balanceLock)
        {
            Console.WriteLine($"Balance before credit:{balance, 5}");
            Console.WriteLine($"Amount to add        :{amount, 5}");
            balance = balance + amount;
            Console.WriteLine($"Balance after credit :{balance, 5}");
        }
    }
}

class AccountTest
{
    static void Main()
    {
        var account = new Account(1000);
        var tasks = new Task[100];
        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() => RandomlyUpdate(account));
        }
        Task.WaitAll(tasks);
    }

    static void RandomlyUpdate(Account account)
    {
        var rnd = new Random();
        for (int i = 0; i < 10; i++)
        {
            var amount = rnd.Next(1, 100);
            bool doCredit = rnd.NextDouble() < 0.5;
            if (doCredit)
            {
                account.Credit(amount);
            }
            else
            {
                account.Debit(amount);
            }
        }
    }
}

C# 言語仕様C# language specification

詳細については、「C# 言語の仕様」を参照してください。For more information, see the C# Language Specification. 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。The language specification is the definitive source for C# syntax and usage.

関連項目See also