lock 陳述式 - 確保共用資源的獨佔存取權
lock
陳述式會取得指定物件的互斥鎖定、執行陳述式區塊,然後釋放鎖定。 持有鎖定時,持有鎖定的執行緒可以再次取得並釋放鎖定。 其他執行緒將無法取得鎖定,並將等待直到釋放鎖定為止。 lock
語句可確保每當有一個執行緒執行其主體時,最多隻能執行一個執行緒。
lock
陳述式格式為
lock (x)
{
// Your code...
}
x
是參考型別的運算式。 其完全等同於
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
擲回例外狀況,也會釋放鎖定。
您無法在await
語句主體 lock
中使用運算式。
指導方針
當您同步處理執行緒對共用資源的存取時,請鎖定專用物件執行個體 (例如 private readonly object balanceLock = new object();
) 或另一個不太可能由程式碼不相關的部分用作鎖定物件的執行個體。 避免對不同的共用資源使用相同的鎖定物件執行個體,因為其可能導致鎖死或鎖定爭用。 特別是,請避免使用下列實例作為鎖定物件:
盡可能縮短鎖定的時間,以減少鎖定爭用。
範例
下列範例會定義 Account
類別,該類別會透過鎖定專用的 balanceLock
執行個體來同步對其私用 balance
欄位的存取。 使用相同的執行個體進行鎖定,可確保嘗試同時呼叫 Debit
或 Credit
方法的兩個執行緒無法同時更新 balance
欄位。
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));
}
}
}
}
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的 lock 陳述式一節。