Ошибка обновления количества с параллельными транзакциями C # - PullRequest
0 голосов
/ 07 ноября 2018

Я разработал приложение для онлайн-покупки моих продуктов. У меня есть продукт "Зонтики" в моем магазине на 100 штук. Я разработал приложение для онлайн-покупки моих продуктов. Но при одновременной покупке возникает проблема.

В случае двух одновременных покупок AvailableQty будет обновляться некорректно. Скажем, есть две транзакции, происходящие одновременно с количеством покупок 100 и 50. В идеале первая транзакция (количество покупок равно 100) должна быть успешной, поскольку у нас есть 100 доступных акций. Но вторая транзакция должна вернуть ошибку, потому что запас недостаточен для обработки, так как с первой транзакцией баланс равен 0. (100 - 100). Но по вышеприведенному сценарию обе транзакции успешны, и теперь баланс показывает -50.

Это будет работать правильно, когда есть две отдельные транзакции. Но это проблема, когда эти две транзакции происходят одновременно. Причиной этой проблемы является то, что когда в параллельных транзакциях условие проверки доступности попадает в одно и то же время, в это время условие выполняется, поскольку таблица БД не обновлена ​​с последним количеством.

Как я могу это исправить?

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

1 Ответ

0 голосов
/ 07 ноября 2018

Это типичная проблема параллелизма потоков, которая может быть решена несколькими способами, одним из которых является использование простого оператора lock:

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

Только один поток может получить эксклюзивные права на блокировку _availableQtyLock, что означает, что другому потоку придется ждать, пока первый поток снимет блокировку этого объекта.

Примите во внимание, что это самый простой (и, возможно, самый медленный) способ работы с параллелизмом, есть и другие способы синхронизации потоков, например, Monitor, Semaphore, быстрый SlimLock и т. Д. ... Поскольку трудно сказать, какой из них лучше всего подойдет вашим потребностям, вам необходимо провести надлежащее тестирование производительности / стресс-тестов, но я бы посоветовал начать с Простейшие.

Примечание. Как уже упоминалось в комментариях, проблемы с параллелизмом могут решаться и на уровне БД, что на самом деле было бы более подходящим, но если вы не хотите / не можете вносить какие-либо изменения в БД, это может быть способом идти

...