在 C# 中使用 lock 语句
Using lock statement in c#
我需要使用锁构造,编辑以下方法并行执行:
public void Withdraw(int amountToWithdraw)
{
if (amountToWithdraw <= 0)
{
throw new ArgumentException("The amount should be greater than 0.");
}
if (amountToWithdraw > MaxAmountPerTransaction)
{
throw new ArgumentException($"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
}
if (amountToWithdraw > Amount)
{
throw new ArgumentException("Insufficient funds.");
}
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
这是结果
private readonly object balanceLock = new object();
public void Withdraw(int amountToWithdraw)
{
if (amountToWithdraw <= 0)
{
throw new ArgumentException("The amount should be greater than 0.");
}
if (amountToWithdraw > MaxAmountPerTransaction)
{
throw new ArgumentException($"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
}
if (amountToWithdraw > Amount)
{
throw new ArgumentException("Insufficient funds.");
}
lock (balanceLock)
{
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
}
这是对方法 WithdrawAndEmulateTransactionDelay 的描述,不应更改
private void WithdrawAndEmulateTransactionDelay(int amountToWithdraw)
{
Thread.Sleep(1000);
Amount -= amountToWithdraw;
}
但是,单元测试失败了。我的代码哪里出错了?
看来,您应该将最后的验证 放在 锁中:在您当前的实现中,可能
- 线程 #1 尝试撤回
cash1
,这是有效的 (cash1 < Account
),验证已通过
- 线程 #2 尝试撤回
cash2
,这是有效的 (cash2 < Account
),验证已通过
- 不过
cash1 + cash2 > Account
- 线程 #1 调用
WithdrawAndEmulateTransactionDelay
,现在 Amount == Amount - cash1 < cash2
- 线程 #2 调用
WithdrawAndEmulateTransactionDelay
;因为 Amount - cash1 < cash2
你的测试失败了
private readonly object balanceLock = new object();
public void Withdraw(int amountToWithdraw) {
// These validations are not depended on Amount, they don't want lock
if (amountToWithdraw <= 0)
throw new ArgumentOutOfRangeException(nameof(amountToWithdraw),
"The amount should be greater than 0.");
if (amountToWithdraw > MaxAmountPerTransaction)
throw new ArgumentOutOfRangeException(nameof(amountToWithdraw),
$"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
// from now on we start using Amount, so we need the lock:
lock (balanceLock) {
if (amountToWithdraw > Amount)
throw new ArgumentException("Insufficient funds.", nameof(amountToWithdraw));
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
}
我也会避免所有这些例外情况。错误的输入是此代码通常是意料之中的,因此并非例外。
试试这个代码:
public TransactionStatus Withdraw(int amountToWithdraw)
{
bool successful = false;
string message = "OK";
int balanceBefore = Amount;
int balanceAfter = Amount;
if (amountToWithdraw <= 0)
{
message = "The amount should be greater than 0.";
}
else if (amountToWithdraw > MaxAmountPerTransaction)
{
message = $"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.";
}
else
{
lock (balanceLock)
{
if (amountToWithdraw > Amount)
{
message = "Insufficient funds.";
}
else
{
Thread.Sleep(1000);
Amount -= amountToWithdraw;
successful = true;
balanceAfter = Amount;
}
}
}
return new TransactionStatus()
{
Successful = successful, Message = message, BalanceBefore = balanceBefore, BalanceAfter = balanceAfter
};
}
public struct TransactionStatus
{
public bool Successful;
public string Message;
public int BalanceBefore;
public int BalanceAfter;
}
我需要使用锁构造,编辑以下方法并行执行:
public void Withdraw(int amountToWithdraw)
{
if (amountToWithdraw <= 0)
{
throw new ArgumentException("The amount should be greater than 0.");
}
if (amountToWithdraw > MaxAmountPerTransaction)
{
throw new ArgumentException($"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
}
if (amountToWithdraw > Amount)
{
throw new ArgumentException("Insufficient funds.");
}
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
这是结果
private readonly object balanceLock = new object();
public void Withdraw(int amountToWithdraw)
{
if (amountToWithdraw <= 0)
{
throw new ArgumentException("The amount should be greater than 0.");
}
if (amountToWithdraw > MaxAmountPerTransaction)
{
throw new ArgumentException($"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
}
if (amountToWithdraw > Amount)
{
throw new ArgumentException("Insufficient funds.");
}
lock (balanceLock)
{
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
}
这是对方法 WithdrawAndEmulateTransactionDelay 的描述,不应更改
private void WithdrawAndEmulateTransactionDelay(int amountToWithdraw)
{
Thread.Sleep(1000);
Amount -= amountToWithdraw;
}
但是,单元测试失败了。我的代码哪里出错了?
看来,您应该将最后的验证 放在 锁中:在您当前的实现中,可能
- 线程 #1 尝试撤回
cash1
,这是有效的 (cash1 < Account
),验证已通过 - 线程 #2 尝试撤回
cash2
,这是有效的 (cash2 < Account
),验证已通过 - 不过
cash1 + cash2 > Account
- 线程 #1 调用
WithdrawAndEmulateTransactionDelay
,现在Amount == Amount - cash1 < cash2
- 线程 #2 调用
WithdrawAndEmulateTransactionDelay
;因为Amount - cash1 < cash2
你的测试失败了
private readonly object balanceLock = new object();
public void Withdraw(int amountToWithdraw) {
// These validations are not depended on Amount, they don't want lock
if (amountToWithdraw <= 0)
throw new ArgumentOutOfRangeException(nameof(amountToWithdraw),
"The amount should be greater than 0.");
if (amountToWithdraw > MaxAmountPerTransaction)
throw new ArgumentOutOfRangeException(nameof(amountToWithdraw),
$"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.");
// from now on we start using Amount, so we need the lock:
lock (balanceLock) {
if (amountToWithdraw > Amount)
throw new ArgumentException("Insufficient funds.", nameof(amountToWithdraw));
WithdrawAndEmulateTransactionDelay(amountToWithdraw);
}
}
我也会避免所有这些例外情况。错误的输入是此代码通常是意料之中的,因此并非例外。
试试这个代码:
public TransactionStatus Withdraw(int amountToWithdraw)
{
bool successful = false;
string message = "OK";
int balanceBefore = Amount;
int balanceAfter = Amount;
if (amountToWithdraw <= 0)
{
message = "The amount should be greater than 0.";
}
else if (amountToWithdraw > MaxAmountPerTransaction)
{
message = $"The value {amountToWithdraw} exceeds transaction limit: {MaxAmountPerTransaction}.";
}
else
{
lock (balanceLock)
{
if (amountToWithdraw > Amount)
{
message = "Insufficient funds.";
}
else
{
Thread.Sleep(1000);
Amount -= amountToWithdraw;
successful = true;
balanceAfter = Amount;
}
}
}
return new TransactionStatus()
{
Successful = successful, Message = message, BalanceBefore = balanceBefore, BalanceAfter = balanceAfter
};
}
public struct TransactionStatus
{
public bool Successful;
public string Message;
public int BalanceBefore;
public int BalanceAfter;
}