C# EF:在多个线程中更新相同的DB表行
我有一个教育任务,不明白我的代码中有什么错误。我正在尝试更新DB表并在事务中隔离此逻辑。最后,我陷入了僵局。有人能解释一下我做错了什么吗?C# EF:在多个线程中更新相同的DB表行,c#,multithreading,entity-framework,C#,Multithreading,Entity Framework,我有一个教育任务,不明白我的代码中有什么错误。我正在尝试更新DB表并在事务中隔离此逻辑。最后,我陷入了僵局。有人能解释一下我做错了什么吗? 这里是我的线程创建逻辑: for (int i = 0; i < 10; i++) { var thread = new Thread(() => UpdateOrder(orderId)); threadList.Add(thread); thread.Start(); } foreach (var t in threa
这里是我的线程创建逻辑:
for (int i = 0; i < 10; i++)
{
var thread = new Thread(() => UpdateOrder(orderId));
threadList.Add(thread);
thread.Start();
}
foreach (var t in threadList)
{
t.Join();
}
//Display sum by order items amount and amount property from order entity
WriteAmount(orderId);
我为每个线程创建了自己的上下文。为什么我的数据库检测到死锁?这是异常消息:“事务(进程ID 51)在另一个进程的锁资源上被死锁,并且已被选为死锁牺牲品。请重新运行该事务。”
提前谢谢 您在数据库方面遇到了问题。 为什么要使用
IsolationLevel.RepeatableRead
事务类型?假设这就是原因。
您可以在此处了解不同的隔离级别:
我建议你:
1.将隔离级别更改为默认值readcommitted
2.删除
order.Amount+=50代码>。相反,在数据库端创建一个触发器来处理OrderItems表上的插入/更新/删除事件,并修改Orders表。如果我设置了IsolationLevel。RepeatableRead
按订单项计算的最终Sum
当然不等于Amount
属性。发生这种情况是因为您试图从并行事务计算它。因此,您正在尝试同时修改来自并行线程的相同数据。即使您很幸运,并且没有从数据库中获得事务死锁,您也会使用不正确的数据更新它。在读取订单金额和将该数字写入数据库(即使是两行连续的代码)之间,订单项目的数量可以更改,因此您需要触发器。它们是在DBMS中设计来维护数据一致性的,所以没有其他方法可以只使用数据库工具来解决多线程问题,对吗?也许我可以在桌子上锁上几排,对吗?嗯。。。有很多方法可以做到这一点。。。在mssql中,您可以使用类似的方法,或者您可以使用Redis提供跨进程锁或e.t.c.或者您可以为每个订单创建一个对象字典,并使用lock
语句来防止对同一订单进行并行处理(但如果您从其他进程或其他方法更新订单,这将失败),但是,从我的角度来看,像这样的一切都有点像直肠。谢谢你的重播。但是我只需要用DB工具来解决这个问题。我已经阅读了updlock和rowlock的相关内容,现在正在考虑如何在当前情况下使用它们。可能是因为所有上下文和事务都需要OrderItems表上的writelock,并且在读取操作完成之前都不会释放其锁。这将导致9个上下文出现死锁-它根本不适用于2PL。另外,请记住DbContext不是线程安全的——在多线程环境中使用它时,不能期望它能够完美地工作。
public static void UpdateOrder(int orderId)
{
using (var db = new OrderContext()
{
using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew
,new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
{
var o1 = new OrderItem { Title = "title", Amount = 50, Count = 1, OrderId = orderId };
db.OrderItems.Add(o1);
var order = db.Orders.Find(orderId); //Include(o => o.Items).Single(o => o.Id == orderId);
order.Amount += 50; //order.Items.Sum(oi => oi.Amount);
db.SaveChanges();
transactionScope.Complete();
}
}
}