在多线程编程中,可能会有多个线程的并发执行同一段代码,但是在某些情况下需要在同一时刻保证只有一个线程执行,避免某些对象的调用冲突或内存使用冲突,这就需要使用到锁。lock关键字可以用来确保代码块完成运行,而不会被其他线程中断。在同一时刻内只允许一个线程进入执行,而其他线程等待。
lock
语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。
当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private readonly object balanceLock = new object();
)或另一个不太可能被代码无关部分用作 lock 对象的实例。 避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。 具体而言,避免将以下对象用作 lock 对象:
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);
}
}
}
}