如何避免多线程方案中的DbContext问题

时间:2016-11-15 12:37:10

标签: c# multithreading entity-framework transactions savechanges

我正在开发一个非常大的项目,该项目应该与一个非常古老的Command Base API进行通信,并执行许多操作,每个操作都必须执行许多命令才能达到目的,然后我有保存结果状态。

每个人都与我的数据库交谈,他们在我的进程队列中添加一条记录,我读取它,执行一些操作,在进程队列本身或在一个受尊重的表中返回一些结果。

主要管道如下图所示,省略了很多动作和锁定。     enter code here

DB是EF DbContext,黑盒子,是主线程中运行的动作,它运行多个动作并反复执行,直到应用程序终止。

Orange Box是在主线程调用后执行操作的子线程。

以前我有一个问题,因为事务导致数据库死了,但是因为我必须在队列中有1个API或者最多2个,而我只是在SQL管理工具下感觉到它,我想到了稍后修好。

但是新的,我觉得这个问题有一段时间了,但今天我看到了,...我的行动失败了,然后应用程序开始重复相同的过程,一遍又一遍,没有终止它或做任何其他,虽然它应该返回成功或错误。所以我跟踪了一会儿,我注意到它失败的地方(内部异常处理程序之一)说我试图将一个重复的密钥插入数据库,所以我去看看Db的变化,我注意到我有超过五个条目,应该只有一个。

所以我首先考虑在每个dB.SaveChange之前获取更改,所以我在网上找到了一些代码,这些代码使所有更改保持不变。 所以我开始更改我的应用程序添加这段代码:

lock(DbAction){
  ClearingDbContextChanges();
  AddResults() if there is any
  Change() if there is any
  Delete() if there is any
  SetEntity as Modified if there is any
  Db.SaveChanges()
}

然后在我运行应用程序之前,我到达最后一个位置,我注意到我不能再使用此操作,并且它使我的一些主要操作无法使用。

1 个答案:

答案 0 :(得分:0)

lock(DbAction) {
   // Modify DB 
}

NOT 混合应用程序锁和数据库锁。你要求一个无法察觉的死锁。锁定等待链有一个循环时发生死锁。 DB能够检测此类周期并解决死锁(通过选择受害者并中止其事务)。但是,当您将应用程序锁和数据库锁组合在一起时,等待链可以通过应用程序锁形成一个循环。 Think Thread A持有DB锁1并等待DbAction,并且线程2持有DbAction并等待DB锁1.这些死锁链是由检测不到的,因此不会引发死锁。你的应用程序将永远保持死锁(直到被明确杀死)。

在持有应用程序lock时不要发出数据库调用。决不。如初。

现在,针对您的问题:您会出现典型的ACID不一致问题。您的工作单元似乎重复执行(DB中的重复条目)和/或在DB中保存不一致状态(缺少事务范围?)。人们无法猜出那里有什么问题。