TransactionScope 代替使用锁

4

我目前有一个方法,该方法读取数据以确定是否需要更新,然后将更新推送到数据库(依赖注入)。 由于多个线程在第一次更新之前读取数据,因此该方法受到了很大的打击,并发相关的错误也随之而来。

我使用锁解决了这个问题,效果非常好。我可以使用TransactionScope来做同样的事情吗?如果我使用TransactionScope,它会像锁一样阻塞另一个线程吗?并且,我是否可以像使用锁一样“锁定”特定的“id”(我保留一个字典,为每个“id”存储要锁定的对象)?

我正在使用Entity Framework 5,尽管其被一个仓储和工作单元模式隐藏了起来。


1
那么考虑使用ReadWriterLockSlim呢?如果你有多个读取操作和少量写入操作,它比锁更好。 (事务范围如何使其线程安全?) - Adriano Repetti
这比仅仅使用锁更好(+1)。但是,它不允许我在其他地方安全地处理数据,或者在同一数据上从另一台机器上工作。 - ccook
1
通常建议使用ReaderWriterLockSlim时 不允许 递归访问,并且如果允许,它仍然可以编写有问题的代码,这是一种危险的情况。就像他解决的那个问题一样危险,甚至更危险。 - Grant Thomas
也许如果我可以让EF上下文(再次在接口后面)具有TransactionScope,防止读/写混杂的话? - ccook
1个回答

2
应用程序级别的锁定可能不是解决此问题的方案。首先,通常只需要锁定单个记录或记录范围。接下来,您可能需要锁定其他修改并进入相当复杂的代码。
这种情况通常使用乐观并发或悲观并发处理。
乐观并发 - 您将拥有一个附加的数据库生成列(数据库通常具有此类特殊类型,例如时间戳或行版本)。每次更新记录时,数据库会自动更新该列。如果将此列配置为行版本,则 EF 将在更新的 where 条件中包含该列 => 执行的更新将搜索具有给定键和行版本的记录。如果找到记录,则会进行更新。如果未找到记录,则表示具有该键的记录不存在或自从当前进程加载其数据以来,其他人已更新了该记录 => 您将收到异常,并可以尝试刷新数据并再次保存更改。此模式适用于不经常更新的记录。在您的情况下,这可能会引起其他麻烦。
悲观并发 - 此模式使用数据库锁定。当您查询记录时,您将锁定它以进行更新,因此其他人不能将其锁定以进行更新或直接更新。不幸的是,目前 EF 没有直接支持此模式,您必须通过原始 SQL 执行它。我写了一篇关于悲观并发及其在 EF 中的使用 的文章。即使对于负载较重的数据库,悲观并发也可能不是一个好的解决方案。
如果你真的建立了一个解决方案,在这个解决方案中,许多并发进程一直试图更新相同的数据,那么你最终可能需要重新设计,因为基于锁定或重新运行失败更新的可靠高性能解决方案将不存在。

感谢您的反馈!目前我所实现的是一个应用程序级别的锁(基于性能,如果TryEnter失败,则跳过“操作”),其中存在一个应用程序范围,使用isolationlevel.repeatableread来处理方法外部的并发问题。这是一个不好的解决方案吗? - ccook

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接