简体   繁体   中英

Update quantity issue with Concurrent Transactions C#

I have developed an application to online purchasing my products. I have a product "Umbrellas" in my store with 100 pieces. I have developed an application to online purchasing my products. But there is an issue when there is a concurrent purchasing.

If there is a two concurrent purchasing happening the AvailableQty will update incorrectly. Let's say there are two transactions happening concurrently with Purchasing Qty as 100 & 50. Ideally, the first transaction (purchase qty is 100) should be successful as we have 100 stocks available. But the second transaction should return an error because the stock is insufficient to process as with the first transaction the balance is 0. (100 - 100). But above scenario both transactions are successful and the balance shows as -50 now.

This will work correctly when there are two separate transactions. But this is an issue when this two transactions happening CONCURRENTLY. The reason for this problem is, when concurrent transactions the condition to check the availability hits same time, in that time the condition is satisfied as the DB table has not updated with the latest qty.

How can I correct this?

public bool UpdateStock(int productId, int purchaseQty)
{
    using(var db = new MyEntities())
    {
       var stock = db.Products.Find(productId);

       if (stock.AvailableQty >= purchaseQty) // Condition to check the availablity
       {
            stock.AvailableQty = stock.AvailableQty - purchaseQty;
            db.SaveChanges();
            return true;
        }
        else
        {
            return false;
        }
    }
}

This is typical thread concurrency issue which can be resolved in multiple ways, one of them is using simple lock statement:

public class StockService
{
    private readonly object _availableQtyLock = new object();

    public bool UpdateStock(int productId, int purchaseQty)
    {
        using (var db = new MyEntities())
        { 
            lock (_availableQtyLock)
            {
                var stock = db.Products.Find(productId);
                if (stock.AvailableQty >= purchaseQty) // Condition to check the availablity
                {
                    stock.AvailableQty = stock.AvailableQty - purchaseQty;
                    db.SaveChanges();
                    return true;
                }
                return false;
            }
        }
    }
}

Only one thread can get a exclusive rights to get a lock on _availableQtyLock , which means other thread will have to wait for the first thread to release lock on that object.

Take into account this is the simplest (and possibly slowest) way of dealing with concurrency, there are other ways to do thread synchronization, eg Monitor , Semaphore , fast SlimLock etc... Since it's hard to tell which one will suit your needs the best, you'll need to do proper performance/stress testing, but my advice would be to start with simplest.

Note: As others mentioned in comments, concurrency issues can be done on DB level as well, which indeed would be more suitable, but if you don't want/can't introduce any DB changes, this would be way to go

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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