问题描述
我将数据集存储在 ASP.Net WebApplication-Cache 中.此 Intranet-app 中的每个用户都使用相同的实例.在插入/更新/删除操作时,数据库将被更新并相应地修改数据集.
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?
编辑:正如您所提到的,我需要在添加/编辑/删除操作时锁定对象.MSDN* 表示 DataSet 对于多个用户是线程安全的.这是什么意思,Dataset 中的 DataTables 也是线程安全的吗?以及如何在写操作上锁定单个数据表而不是整个数据集?
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 - 多线程编程
ADO.NET 针对性能、吞吐量和可扩展性进行了优化.结果,ADO.NET 对象不锁定资源,只能在单个线程上使用.唯一的那个例外是 DataSet,它对多个读取器是线程安全的.但是,您需要在写入期间锁定 DataSet.
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
感谢 casperOne,我通过以下方式修改了插入/更新和删除操作(dsRma 是一个数据集):
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
首先,如果另一个线程尝试枚举 RMA-Table,它现在可以工作吗?其次,锁定更新的数据行而不是锁定整个数据表是否足够(见下文)?
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
推荐答案
一般情况下,枚举序列时,不能执行会修改序列的操作.
Generally, when enumerating over a sequence, you cannot perform operations that will modify the sequence.
因为您将集合存储在 WebApplication 缓存中,所以它可以同时被多个线程命中;您可能有一个线程请求在缓存中输入一个项目,而另一个线程正在尝试对其进行枚举.
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.
您有几个选择.第一个也是最简单的方法是将对序列的访问封装在 lock
块中.您必须将添加/编辑/删除操作包装在单独的块中,并且您还必须在 entire 序列的枚举上的 lock
块中定位相同的对象.
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 a lock
block over the enumeration of the entire sequence.
这可以很容易地封装在另一个类中,然后您将其存储在缓存中.
This can easily be encapsulated in another class and then you store that in your cache.
如果与写入操作相比,您还执行大量读取操作,那么您可以使用 ReaderWriterLockSlim
类适当地锁定读写;上面的策略将锁定两个并发读取.
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.
这篇关于缓存中的数据集:集合已修改;枚举操作可能无法执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!