Pernyataan kunci - memastikan akses eksklusif ke sumber daya bersama
Pernyataan lock
memperoleh kunci pengecualian bersama untuk objek tertentu, menjalankan blok pernyataan, lalu melepaskan kunci. Saat kunci ditahan, utas yang menahan kunci dapat kembali memperoleh dan melepas kunci. Setiap utas lain diblokir dari memperoleh kunci dan menunggu sampai kunci dilepaskan. Pernyataan ini lock
memastikan bahwa maksimal hanya satu utas yang menjalankan isinya kapan saja.
Pernyataan mengambil lock
formulir berikut:
lock (x)
{
// Your code...
}
Variabel x
adalah ekspresi jenis System.Threading.Lock , atau jenis referensi. Ketika x
diketahui pada waktu kompilasi untuk menjadi jenis System.Threading.Lock, justru setara dengan:
using (x.EnterScope())
{
// Your code...
}
Objek yang dikembalikan oleh Lock.EnterScope() adalah ref struct
yang menyertakan Dispose()
metode . Pernyataan yang dihasilkan using
memastikan cakupan dirilis bahkan jika pengecualian dilemparkan dengan isi lock
pernyataan.
Jika tidak, lock
pernyataan tersebut justru setara dengan:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Karena kode menggunakan try-finally
pernyataan, kunci dilepaskan bahkan jika pengecualian dilemparkan dalam isi lock
pernyataan.
Anda tidak dapat menggunakan await
ekspresi dalam isi lock
pernyataan.
Panduan
Dimulai dengan .NET 9 dan C# 13, kunci instans objek khusus dari jenis System.Threading.Lock untuk performa terbaik. Selain itu, pengkompilasi mengeluarkan peringatan jika objek yang diketahui Lock
dilemparkan ke jenis lain dan dikunci. Jika menggunakan versi .NET dan C#yang lebih lama, kunci instans objek khusus yang tidak digunakan untuk tujuan lain. Hindari menggunakan instans objek kunci yang sama untuk sumber daya bersama yang berbeda, karena dapat mengakibatkan kebuntuan atau perselisihan kunci. Secara khusus, hindari menggunakan instans berikut sebagai objek kunci:
this
, karena penelepon mungkin juga menguncithis
.- Type instans, karena mungkin diperoleh oleh operator atau refleksi typeof .
- instans string, termasuk literal string, karena mungkin diinternasi.
Tahan kunci untuk waktu sesingkat mungkin untuk mengurangi perselisihan kunci.
Contoh
Contoh berikut mendefinisikan kelas Account
yang menyinkronkan akses ke bidang privatnya balance
dengan mengunci instans khusus balanceLock
. Menggunakan instans yang sama untuk penguncian memastikan bahwa dua utas yang berbeda tidak dapat memperbarui balance
bidang dengan memanggil Debit
metode atau Credit
secara bersamaan. Sampel menggunakan C# 13 dan objek baru Lock
. Jika Anda menggunakan versi C# yang lebih lama atau pustaka .NET yang lebih lama, kunci instans 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));
}
}
}
}
Spesifikasi bahasa C#
Untuk informasi selengkapnya, lihat bagian pernyataan lock dari spesifikasi bahasa C#.