.net 缓存中的数据集:已修改集合;枚举操作可能无法执行

.net 缓存中的数据集:已修改集合;枚举操作可能无法执行,.net,asp.net,multithreading,caching,ado.net,.net,Asp.net,Multithreading,Caching,Ado.net,我正在ASP.NETWebApplication缓存中存储数据集。此intranet应用程序中的每个用户都使用相同的实例。在执行插入/更新/删除操作时,将更新数据库并相应修改数据集 但很少有例外情况表明我错过了什么。我认为这一定与线程安全有关 Collection was modified; enumeration operation might not execute 在我访问数据集中数据表的行中,例如: Dim view As New DataView(dsERP.ERP_Charge,

我正在ASP.NETWebApplication缓存中存储数据集。此intranet应用程序中的每个用户都使用相同的实例。在执行插入/更新/删除操作时,将更新数据库并相应修改数据集

但很少有例外情况表明我错过了什么。我认为这一定与线程安全有关

 Collection was modified; enumeration operation might not execute
在我访问数据集中数据表的行中,例如:

Dim view As New DataView(dsERP.ERP_Charge, filter, sort, _
    Data.DataViewRowState.CurrentRows)
当视图枚举datatable时,它显然被另一个线程更改了

使此线程安全的最佳方法是什么

编辑:如您所述,我需要在添加/编辑/删除操作中锁定对象。 *表示数据集对于多个用户是线程安全的。这意味着什么,数据集中的数据表也是线程安全的吗?如何在写操作上锁定单个数据表而不是整个数据集

*ADO.NET-多线程编程

ADO.NET针对性能、吞吐量和可伸缩性进行了优化。因此 ADO.NET对象不锁定资源,只能在单个线程上使用。那个 数据集是一个例外,它对多个读卡器是线程安全的。然而,你需要 在写入期间锁定数据集

这是返回数据集的属性:

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是一个数据集):

首先,如果另一个线程尝试枚举RMA表,它现在是否工作?其次,锁定更新的datarow而不是锁定整个datatable是否足够(见下文)


通常,在序列上枚举时,不能执行修改序列的操作

因为您将集合存储在WebApplication缓存中,所以它可能同时被多个线程命中;您可能在一个线程上有一个请求,在另一个线程尝试枚举时在缓存中输入一个项

要解决这个问题,您确实应该使对数据结构的访问线程安全

你有几个选择。第一种也是最简单的方法是将对序列的访问封装在
lock
块中。您必须将添加/编辑/删除操作包装在各个块中,并且在整个序列的枚举中,您还必须在
锁定
块中以相同对象为目标

这可以很容易地封装在另一个类中,然后将其存储在缓存中

如果与写操作相比,您执行的读操作数量也较多,那么您可以使用该类适当地锁定读写操作;上述策略将锁定两个并发读取


另一个解决方案是锁定缓存上的任何添加/编辑/更新操作,当您想要枚举缓存时,锁定并创建序列本身的副本并返回该副本;返回序列副本后,可以单独枚举该序列,因为该序列与实际修改的序列不同。

通常,在对序列进行枚举时,不能执行修改序列的操作

因为您将集合存储在WebApplication缓存中,所以它可能同时被多个线程命中;您可能在一个线程上有一个请求,在另一个线程尝试枚举时在缓存中输入一个项

要解决这个问题,您确实应该使对数据结构的访问线程安全

你有几个选择。第一种也是最简单的方法是将对序列的访问封装在
lock
块中。您必须将添加/编辑/删除操作包装在各个块中,并且在整个序列的枚举中,您还必须在
锁定
块中以相同对象为目标

这可以很容易地封装在另一个类中,然后将其存储在缓存中

如果与写操作相比,您执行的读操作数量也较多,那么您可以使用该类适当地锁定读写操作;上述策略将锁定两个并发读取


另一个解决方案是锁定缓存上的任何添加/编辑/更新操作,当您想要枚举缓存时,锁定并创建序列本身的副本并返回该副本;返回序列副本后,您可以单独枚举该序列,因为该序列与实际修改的序列不同。

为什么要缓存数据集?@Lasse:主要是为了避免对任何用户的每次回发都进行永久性数据库访问。但是我对这个决定不再很满意,因为像这样的问题。此外,大多数操作都是插入和更新,因此我也必须更新数据库。通过向其中添加锁,可以有效地序列化对数据集的所有访问,基本上可以将多用户web应用程序转变为一次一个用户的应用程序。这真的是你想要的吗?如果没有,请丢弃缓存的数据集,与数据库对话。缓存不变的内容,并为那些不变的内容访问数据库,让它担心锁定什么以及何时锁定。毕竟,这是数据库最擅长的地方。为什么要缓存数据集?@Lasse:主要是为了避免在任何用户的每次回发上都永久访问数据库。但是我对这个决定不再很满意,因为像这样的问题。此外,大多数操作都是插入和更新,因此我也必须更新数据库。通过向其中添加锁,可以有效地序列化对数据集的所有访问,基本上可以将多用户web应用程序转变为一次一个用户的应用程序。这真的是你想要的吗?如果没有,请丢弃缓存的数据集,与数据库对话。缓存那些不变的东西,为那些不变的东西访问数据库,让它担心一点
Dim success As Boolean
SyncLock dsRMA.RMA
     success = insert()
End SyncLock
Dim thisRMA As ERPModel.dsRMA.RMARow = dsRMA.RMA.FindByIdRMA(Me.IdRma)
Dim success As Boolean
SyncLock thisRMA
       success = update(thisRMA)
End SyncLock