【问题标题】:SqlDataAdapter.Fill - Asynchronous approachSqlDataAdapter.Fill - 异步方法
【发布时间】:2010-01-21 11:50:06
【问题描述】:

使用 C#/.NET 3.5。

目前我正在使用 SqlDataAdapter.Fill() 一个接一个地填充 2 个数据表。

我想并行填充这两个数据表,同时通过异步执行每个数据表。但是,Fill() 方法没有异步版本 - 即 BeginFill() 会很棒!

我尝试过的一种方法是(伪):

  1. SqlCommand1.BeginExecuteReader // 第一个查询,用于 DataTable1
  2. SqlCommand2.BeginExecuteReader // 第二次查询,用于 DataTable2
  3. SqlCommand1.EndExecuteReader
  4. SqlCommand2.EndExecuteReader
  5. DataTable1.Load(DataReader1)
  6. DataTable2.Load(DataReader2)

但是,DataTable.Load() 需要很长时间:
完成第 1 步到第 4 步需要 3 秒。
然后步骤 5 需要 22 秒。
第 6 步需要 17 秒。
因此,第 5 步和第 6 步总共需要 39 秒。

最终结果是,与一个接一个地执行 2 个 SqlDataAdapter.Fills 相比,这对我没有任何好处。我希望最终结果是整个过程只需要最长的查询(或尽可能接近)。

寻找推荐的方法来最终获得真正异步的方法来填充 DataTable。

还是我自己管理它并滚动 2 个单独的线程,每个线程填充一个 DataTable?

【问题讨论】:

    标签: c# .net asynchronous datatable


    【解决方案1】:

    我建议为每个单独的工作线程。你可以使用ThreadPool.QueueUserWorkItem

    List<AutoResetEvent> events = new List<AutoResetEvent>();
    
    AutoResetEvent loadTable1 = new AutoResetEvent(false);
    events.Add(loadTable1);
    ThreadPool.QueueUserWorkItem(delegate 
    { 
         SqlCommand1.BeginExecuteReader;
         SqlCommand1.EndExecuteReader;
         DataTable1.Load(DataReader1);
         loadTable1.Set();
    });
    
    AutoResetEvent loadTable2 = new AutoResetEvent(false);
    events.Add(loadTable2);
    ThreadPool.QueueUserWorkItem(delegate 
    { 
         SqlCommand2.BeginExecuteReader;
         SqlCommand2.EndExecuteReader;
         DataTable2.Load(DataReader2);
         loadTable2.Set();
    });
    
    // wait until both tables have loaded.
    WaitHandle.WaitAll(events.ToArray());
    

    【讨论】:

    • 所以,一旦我把每一个都排好队,我怎么能等到两个都完成呢?我需要先填充两个表,然后才能继续处理它们
    • 如果有帮助,我已将等待概念添加到我的答案中。
    • @AdaTheDev,您将使用在工作完成后触发的 AutoResetEvents(在每个单独的线程中)。请参阅@Neils 的答案,因为他已经提供了一个示例。
    • 我已更新以向您展示(如果使用我的示例)您将如何实现这一点。
    • 谢谢(两者)。我在 STA 线程上运行,这意味着我不能使用 WaitHandle.WaitAll(“不支持 STA 线程上的多个句柄的 WaitAll”)。有其他选择吗?
    【解决方案2】:

    这是因为DataTable 有很多对象要创建(行、值)。您应该在不同的线程中完成适配器的执行和数据表的填充,并在继续之前同步等待每个操作完成。

    以下代码是用记事本编写的,可能甚至无法编译,但希望您能理解...

    // Setup state as a parameter object containing a table and adapter to use to populate that table here
    
    void DoWork()
    {
        List<AutoResetEvent> signals = GetNumberOfWaitHandles(2);
    
        var params1 = new DataWorkerParameters
            {
                Command = GetCommand1();
                Table = new DataTable();
            }
    
        var params2 = new DataWorkerParameters
            {
                Command = GetCommand2();
                Table = new DataTable();
            }
    
        ThreadPool.QueueUserWorkItem(state => 
            {
                var input = (DataWorkerParameters)state;
                PopulateTable(input);
                input.AutoResetEvent.Set(); // You can use AutoResetEvent.WaitAll() in the caller to wait for all threads to complete
            },
            params1
        );
    
        ThreadPool.QueueUserWorkItem(state => 
            {
                var input = (DataWorkerParameters)state;
                PopulateTable(input);
                input.AutoResetEvent.Set(); // You can use AutoResetEvent.WaitAll() in the caller to wait for all threads to complete
            },
            params2
        );
    
        WaitHandle.WaitAll(signals.ToArray());
    }
    
    
    void PopulateTable(DataWorkerParameters parameters)
    {
        input.Command.ExecuteReader();
        input.Table.Load(input.Command);
    }
    

    【讨论】:

    • 谢谢(两者)。我在 STA 线程上运行,这意味着我不能使用 WaitHandle.WaitAll(“不支持 STA 线程上的多个句柄的 WaitAll”)。有其他选择吗?
    • 你可以提供一个Action 回调,它只在被调用足够多次时才执行下一段代码?不是很好,但应该可以。
    • 为建议和帮助干杯。我有足够的工作来做我想做的事(并行运行查询并没有真正的好处,因为它们相互竞争)。为答案+1,因为它确实有帮助。
    猜你喜欢
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 2010-11-27
    • 2013-06-12
    相关资源
    最近更新 更多