【问题标题】:DataTable and thread safety数据表和线程安全
【发布时间】:2011-07-19 08:55:46
【问题描述】:

我将 DataTable 存储在 ASP .NET 缓存属性中。可以对该 DataTable 执行的操作是:

  • 绑定到网格控件(第 3 方网格内部管理数据源对象,回发后其 DataSource 为 NULL,我假设一旦绑定数据,它就不再使用数据源 DataTable)
  • 从 DataTable 中删除行 (Row.Delete()

我在显式处理该 DataTable 实例时添加了基本的读取器/写入器锁,但我想知道该解决方案是否存在任何其他线程安全问题。我猜当网格控件位于 DataBinding 中间时可能会出现问题,而其他线程删除行?如果是这样,我如何同步对该表的访问,以便在绑定网格控件时不进行 Delete 方法调用?是否有任何事件组合可以放置 AcquireWriterLock 和 ReleaseWriterLock 方法?

谢谢,帕维尔

【问题讨论】:

    标签: .net asp.net multithreading ado.net thread-safety


    【解决方案1】:

    如果你是通过数据绑定来暴露数据表,那就别管它了;你不能使那个线程安全。即使您以某种方式包装DataView(在自定义ITypedList 中),这还不够——数据绑定对数据做出假设,特别是IList 等——例如,它不是将在迭代数据或在 UI 线程上添加一行的过程中以线程竞争的方式随机更改长度。

    规定通过事件在同一线程上进行更改...但不是跨线程。

    【讨论】:

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

    正如人们在其他答案中提到的那样:

    • 通常您只缓存不可变数据(或至少:一旦存在就将其视为不可变数据)。否则它不是缓存
    • 如果有人要编辑公共数据,请勿向公共数据公开多个请求
    • 如果您是通过数据绑定公开数据表,那就别管它了;你不能让那个线程安全

    但是我确实需要缓存一个 DataTable。原因:

    • 获取结果的查询非常庞大且耗时。使用该查询进行调用的页面的刷新率也很大。结果,数据库引擎变得忙碌。通过缓存,我每 2 分钟只会获得 1 个数据库引擎调用
    • 该查询的结果不同。但是用户在 2 分钟内看到相同的结果是完全可以接受的。因此将数据在缓存中存储 2 分钟是可以接受的。也没有其他问题,如并发、乐观/悲观离线锁......

    但是,当用户对其看到的数据进行操作时,会对数据进行一些更改:

    • 必须在 db 上应用该更改。在实现缓存之前,应用程序更改 db 后再次发出巨大的查询以获得结果,只有细微的差异
    • 现在,通过缓存,更改应用于 db 并应用于缓存的 DataTable。然后该数据表再次绑定到数据绑定控件。好处:无需调用 WCF,使用海量查询获取数据 + 将数据表传输到 Web 应用程序

    这就是我为该解决方案实现锁定的方式:

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

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

      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();
          }
      }
      
    3. 这是为数据绑定公开数据的唯一一段代码:

      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;
      }
      

    线程安全并且工作起来就像一个魅力(我是对的吗?:))。但它非常符合我的要求。

    【讨论】:

      【解决方案3】:

      这是一种以线程安全的方式添加行的简单方法。其中 dt = 我的 DataTable 和 dr = 我的 DataRow

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-09
        • 2014-03-04
        相关资源
        最近更新 更多