简体   繁体   English

在c#中使用lock语句

[英]Using lock statement in c#

I need to use the lock construction, and edit the following methods to execute in parallel:我需要使用锁结构,并编辑以下方法以并行执行:

    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);
    
            }

Here is the result这是结果

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);
            }

        }

This is a description of the method WithdrawAndEmulateTransactionDelay which shouldn't be changed这是对不应更改的方法 WithdrawAndEmulateTransactionDelay 的描述

private void WithdrawAndEmulateTransactionDelay(int amountToWithdraw)
        {
            Thread.Sleep(1000);
            Amount -= amountToWithdraw;
        }

However, the unit test failed.但是,单元测试失败了。 Where is the mistake in my code?我的代码中的错误在哪里?

It seems, that you should put the last validation within the lock: in your current implementation it's possible that看来,您应该将最后一个验证放在锁中:在您当前的实现中,有可能

  1. Thread #1 tries to withdraw cash1 , which is valid ( cash1 < Account ), validation's passed线程 #1 尝试提取有效的cash1 ( cash1 < Account ),验证通过
  2. Thread #2 tries to withdraw cash2 , which is valid ( cash2 < Account ), validation's passed线程 #2 尝试提取有效的cash2 ( cash2 < Account ),验证通过
  3. However cash1 + cash2 > Account但是cash1 + cash2 > Account
  4. Thread #1 calls for WithdrawAndEmulateTransactionDelay , now Amount == Amount - cash1 < cash2线程 #1 调用WithdrawAndEmulateTransactionDelay ,现在Amount == Amount - cash1 < cash2
  5. Thread #2 calls for WithdrawAndEmulateTransactionDelay ;线程#2 调用WithdrawAndEmulateTransactionDelay since Amount - cash1 < cash2 you have the test failed因为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);
  } 
}

I'd also avoid all of those exceptions.我也会避免所有这些例外。 Bad input is this code is often to be expected so it's not exceptional.错误的输入是这个代码通常是意料之中的,所以它并不例外。

Try this code:试试这个代码:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM