2013-03-05 65 views
2

我目前有一个方法,它读取数据以确定是否需要更新,然后将更新推送到数据库(注入依赖项)。该方法非常困难,我发现并发性相关的错误,即多个更新,因为多个线程在第一次更新之前读取数据。TransactionScope而不是使用锁

我用锁来解决这个问题,它工作的很好。我该如何使用TransactionScope来做同样的事情?我可以吗?它会锁定另一个线程吗?此外,我可以'锁定'一个特定的'id',因为我正在使用一个锁(我保存一个Dictionary来存储一个对象来锁定每个id)?

我使用实体框架5,虽然它隐藏了一个存储库和工作单元模式。

+1

ReadWriterLockSlim呢?如果你有多个读取和少量写入,它比锁定要好得多。 (如何使TransactionScope线程安全?) – 2013-03-05 16:36:16

+0

这将比只是锁(+1)更好。但是,它不允许我在其他地方安全地处理数据,或者在另一台机器上处理同一数据。 – ccook 2013-03-05 16:38:05

+1

ReaderWriterLockSlim通常建议使用_允许不允许递归访问,并且如果允许的话,允许编写有问题的代码,这是一种危险的情况。就像他解决的问题一样危险,如果不是更多的话。 – 2013-03-05 16:38:07

回答

2

应用程序级锁定可能不是解决此问题的方法。首先,您通常只需要锁定单个记录或记录范围。接下来,您可能稍后需要锁定其他修改并进入相当复杂的代码。

这种情况通常用乐观或悲观并发处理。

  • 乐观并发 - 您将有额外的数据库生成列(数据库通常具有像时间戳或行转换那样的特殊类型)。每次更新记录时,数据库都会自动更新该列。如果将此列配置为行版本EF将在更新的where条件中包含该列=>执行的更新将搜索具有给定密钥和行版本的记录。如果找到记录,它将被更新。如果未找到记录,则表示记录中不存在关键字或者其他人已更新记录,因为当前进程已加载其数据=>您将得到异常,您可以尝试刷新数据并再次保存更改。此模式对于未更新太多的记录很有用。在你的情况下,它可能会导致另一个麻烦。
  • 悲观并发 - 此模式使用数据库锁定代替。当您查询记录时,您将锁定它进行更新,以便其他人无法将其锁定以便直接进行更新或更新。不幸的是,这种模式目前在EF中没有直接支持,您必须通过原始SQL执行。我写了一个article explaining the pessimistic concurrency及其与EF的用法。对于重负载下的数据库,即使是悲观并发可能也不是一个好的解决方案。

如果你真的建立地方很多并发进程的尝试更新同一数据的所有可能与重新设计结束时的解决方案,因为根据锁定或重新运行失败的更新将不会有可靠的高性能解决方案。

+0

感谢您的反馈!我目前所推出的是应用程序级锁的组合(基于性能,如果TryEnter失败,则会跳过'action'),其中存在应用程序作用域并使用isolationlevel.repeatableread来处理罕见的并发情况来自该方法之外的问题。不好的解决方案? – ccook 2013-03-05 19:57:22