缓存中的数据集:集合被修改;枚举操作可能无法执行
我将数据集存储在 ASP.Net WebApplication-Cache 中。此 Intranet 应用程序中的每个用户都使用相同的实例。在插入/更新/删除操作时,数据库将被更新,数据集也会相应地修改。
但我很少会遇到表明我错过了什么的异常。我认为它一定与线程安全有关。
Collection was modified; enumeration operation might not execute
例如,在我访问数据集中的数据表的行中:
Dim view As New DataView(dsERP.ERP_Charge, filter, sort, _
Data.DataViewRowState.CurrentRows)
当视图枚举数据表时,它显然被另一个线程更改了。
使该线程安全的最佳方法是什么?
编辑:正如您所提到的,我需要在添加/编辑/删除操作时锁定对象。 MSDN* 表示 DataSet 对于多个用户来说是线程安全的。这是什么意思,Dataset中的DataTable也是线程安全的吗?以及如何在写入操作上锁定单个数据表而不是整个数据集?
*ADO.NET - 多线程编程
ADO.NET 针对性能、吞吐量和可扩展性进行了优化。结果, ADO.NET 对象不锁定资源,并且只能在单个线程上使用。那个 DataSet 是个例外,它对于多个读取器来说是线程安全的。然而,你需要 在写入期间锁定数据集。
这是返回数据集的属性:
Public ReadOnly Property dsERP() As ERPModel.dsERP
Get
If Cache("DS_ERP") Is Nothing Then
Cache("DS_ERP") = New ERPModel.dsERP
FillDataSet()
End If
Return DirectCast(Cache("DS_ERP"), ERPModel.dsERP)
End Get
End Property
感谢 casperOne,我通过以下方式修改了插入/更新和删除操作(dsRma 是一个数据集):
Dim success As Boolean
SyncLock dsRMA.RMA
success = insert()
End SyncLock
首先,如果另一个线程尝试枚举 RMA-Table,它现在可以工作吗?其次,锁定更新的数据行而不是锁定整个数据表是否足够(见下文)?
Dim thisRMA As ERPModel.dsRMA.RMARow = dsRMA.RMA.FindByIdRMA(Me.IdRma)
Dim success As Boolean
SyncLock thisRMA
success = update(thisRMA)
End SyncLock
I'm storing a dataset in an ASP.Net WebApplication-Cache. Every user in this intranet-app uses the same instance. On insert/update/delete-actions the database will be updated and the dataset is modified accordingly.
But rarely I get an exception that indicates that I've missed something. I assume that it must have something to do with thread safety.
Collection was modified; enumeration operation might not execute
In lines where i access a DataTable in the Dataset, for example:
Dim view As New DataView(dsERP.ERP_Charge, filter, sort, _
Data.DataViewRowState.CurrentRows)
It was apparently changed by another thread while the view enumerates the datatable.
What is the best way to make this thread safe?
Edit: as you've mentioned i need to lock the objects on add/edit/delete operations.
MSDN* says that a DataSet is thread-safe for multiple users. What does this mean, are the DataTables in the Dataset also thread-safe? And how to lock a single datatable on write-operations and not the whole dataset?
*ADO.NET - Multithreaded Programming
ADO.NET is optimized for performance, throughput, and scalability. As a result, the
ADO.NET objects do not lock resources and must only be used on a single thread. The one
exception is the DataSet, which is thread-safe for multiple readers. However, you need
lock the DataSet during writes.
This is the property that returns the dataset:
Public ReadOnly Property dsERP() As ERPModel.dsERP
Get
If Cache("DS_ERP") Is Nothing Then
Cache("DS_ERP") = New ERPModel.dsERP
FillDataSet()
End If
Return DirectCast(Cache("DS_ERP"), ERPModel.dsERP)
End Get
End Property
Thanks to casperOne i've modified the insert/update and delete operations in the following way(dsRma is a dataset):
Dim success As Boolean
SyncLock dsRMA.RMA
success = insert()
End SyncLock
First, does it work now if another thread tries to enumerate the RMA-Table? Second, is it sufficient to lock the datarow that gets updated instead of locking the whole datatable(see below)?
Dim thisRMA As ERPModel.dsRMA.RMARow = dsRMA.RMA.FindByIdRMA(Me.IdRma)
Dim success As Boolean
SyncLock thisRMA
success = update(thisRMA)
End SyncLock
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
通常,在枚举序列时,您不能执行会修改序列的操作。
因为您将集合存储在 WebApplication 缓存中,所以它可以同时被多个线程命中;您可能会在一个线程上请求输入缓存中的一项,而另一个线程则尝试枚举该项。
为了解决这个问题,您确实应该对数据结构进行线程安全的访问。
为此,您有几种选择。第一个也是最简单的方法是将序列的访问封装在
lock
块中。您必须将添加/编辑/删除操作包装在各个块中,并且还必须在整个序列的枚举上的lock
块中定位相同的对象。这可以很容易地封装在另一个类中,然后将其存储在缓存中。
如果与写入操作相比,您还执行大量读取操作,则可以使用
ReaderWriterLockSlim
类来适当锁定读取和写入;上述策略将锁定两个并发读取。另一个解决方案是锁定缓存上的任何添加/编辑/更新操作,当您想要枚举缓存时,锁定并创建序列本身的副本并返回;一旦返回序列的副本,您就可以单独枚举该序列,因为该序列与您实际修改的序列不同。
Generally, when enumerating over a sequence, you cannot perform operations that will modify the sequence.
Because youa re storing the collection in the WebApplication cache, it can be hit by multiple threads at the same time; you might have a request on one thread enter an item in the cache while another is trying to enumerate over it.
To combat this, you really should make access to the data structure thread-safe.
You have a few options for this. The first, and easiest, would be to encapsulate access to the sequence in a
lock
block. You have to wrap add/edit/delete operations in individual blocks, and you also have target the same object in alock
block over the enumeration of the entire sequence.This can easily be encapsulated in another class and then you store that in your cache.
If you are also performing a high number of read operations compared to write operations, then you can use the
ReaderWriterLockSlim
class to lock reads and writes appropriately; the strategy above will lock two concurrent reads out.Another solution would be to lock around any add/edit/update operation on the cache, and when you want to enumerate the cache, lock and create a copy of the sequence itself and return that; once you return the copy of the sequence, you can enumerate that separately as that sequence is different from the one you are actually modifying.