.net 数据表与线程安全

.net 数据表与线程安全,.net,asp.net,multithreading,ado.net,thread-safety,.net,Asp.net,Multithreading,Ado.net,Thread Safety,我正在ASP.NET缓存属性中存储DataTable。可以在该数据表上执行的操作包括: 绑定到网格控件(第三方网格在内部管理数据源对象,回发后其数据源为空,我假设数据绑定后不再使用数据源DataTable) 正在从数据表中删除行(Row.Delete() 我在显式地处理DATABLE实例时添加了基本的读写器锁,但是我想知道这个解决方案还有其他线程安全问题吗?我猜当网格控件处于数据绑定的中间时,可能会出错,其他线程会删除行吗?如果是,我如何同步访问那个表,以便没有删除< /s?trong>方法

我正在ASP.NET缓存属性中存储DataTable。可以在该数据表上执行的操作包括:

  • 绑定到网格控件(第三方网格在内部管理数据源对象,回发后其数据源为空,我假设数据绑定后不再使用数据源DataTable)
  • 正在从数据表中删除行(Row.Delete()

我在显式地处理DATABLE实例时添加了基本的读写器锁,但是我想知道这个解决方案还有其他线程安全问题吗?我猜当网格控件处于数据绑定的中间时,可能会出错,其他线程会删除行吗?如果是,我如何同步访问那个表,以便没有<强>删除< /s?trong>方法调用是在网格控件绑定时进行的?是否有任何事件组合可以放置AcquireWriterLock和ReleaseWriterLock方法


谢谢,Pawel

如果您通过数据绑定公开datatable,那么请忘记它;您无法使该线程安全。即使您以某种方式(在自定义的
ITypedList
中)包装
DataView
)数据绑定对数据进行了假设,特别是<代码> IIST < /代码>等。例如,在迭代数据或在UI线程上添加行时,不会以线程竞争的方式随机改变长度。
在同一个线程上,可以通过事件进行更改…但不能跨线程。

正如其他答案中提到的:

  • 通常,您只缓存不可变的数据(或者至少:一旦数据存在,就将其视为不可变的),否则它就不是缓存
  • 如果有人要编辑公共数据,请不要将多个请求公开给公共数据
  • 如果您是通过数据绑定公开datatable,那么请忘记它;您无法使该线程安全
但是,我确实需要缓存数据表。原因:

  • 获取结果的查询非常庞大且耗时。使用该查询进行调用的页面的刷新率也非常高。因此,db引擎变得繁忙。使用缓存,我每2分钟只能获得1个db引擎调用
  • 该查询的结果不同。但用户完全可以在2分钟内看到相同的结果。因此,将数据存储在缓存中2分钟是可以接受的。此外,没有其他问题,如并发性、乐观/悲观脱机锁
但是,当用户对其看到的数据进行操作时,会对数据进行一些更改:

  • 在实现缓存之前,在应用了对db的更改之后,应用程序再次发出巨大的查询,以获得结果,但差别很小
  • 现在,通过缓存,更改应用于数据库并应用于缓存的数据表。然后,该数据表再次绑定到数据绑定控件。好处:无需进行WCF调用,使用大型查询获取数据+将数据表传输到web应用程序
这就是我为该解决方案实现锁定的方式:

  • 缓存数据存储在类似于单例的包装器中:

    public class AllocationQueue
    {
        private static object tableSyncRoot = new object();
    
  • 这是唯一一段修改缓存数据表的代码:

    internal void RemoveTaskRowFromAllocationQueue(Guid queueId, Guid taskId)
    {
        var allocationQueueEntry = GetAllocationQueueEntry(queueId);
        var queueData = allocationQueueEntry.TaskIdIndexedView;
        lock(tableSyncRoot)
        {
            int rowIndex = queueData.Find(new object[] { taskId });
            queueData[rowIndex].Delete();
        }
    }
    
  • 这是唯一一段公开数据绑定数据的代码:

    public DataTable GetAllocationQueue(Guid queueId, string filter)
    {
        var allocationQueueEntry = GetAllocationQueueEntry(queueId);
        lock (tableSyncRoot)
        {
            var rows = allocationQueueEntry.Table.Select(filter);
            if (rows.Length > 0)
            {
                return rows.CopyToDataTable<DataRow>();
            }
        }
        return null;
    }
    
    公共数据表GetAllocationQueue(Guid queueId,字符串筛选器) { var allocationQueueEntry=GetAllocationQueueEntry(queueId); 锁(表同步器) { var rows=allocationQueueEntry.Table.Select(筛选器); 如果(rows.Length>0) { 返回行。CopyToDataTable(); } } 返回null; }

  • 线程安全&工作起来像个符咒(我说的对吗?:))。但是它非常符合我的要求。

    这里有一个以线程安全的方式添加行的简单方法。其中dt=我的数据表,dr=我的数据行

    lock (dt.Rows.SyncRoot)
    {
        dt.Rows.Add(dr);
    } 
    

    谢谢更改列值怎么样?它也会引起问题吗?我可以添加列IsDeleted,并且不使用该标志集呈现行。@dragonfly,但同样,您很可能最终从非UI线程引发事件,绑定将尝试对其作出反应-再次在非UI线程上。这反过来很可能会带来坏消息,而不是好消息。那么,我如何以安全的方式实现缓存。。。。。?DataTable.Copy()并对该副本进行绑定?关于非UI线程。我们谈论的是ASP.NET应用程序,所以这不是问题所在,是吗?@dragonfly-通常,您只缓存不可变的数据(或者至少:一旦数据存在,就将其视为不可变数据)。否则它不是缓存。。。