2010-01-21 196 views
8

使用C#/ .NET 3.5。SqlDataAdapter.Fill - 异步方法

目前我使用SqlDataAdapter.Fill()逐个填充2个DataTables。

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

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

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

然而,DataTable.Load()需要很长的时间:
它需要3秒执行步骤1〜步骤4
步骤5然后采取22秒。
步骤6需要17秒。
因此,步骤5和步骤6合计39秒。

最终的结果是,这并没有比仅仅执行2个SqlDataAdapter.Fills更有优势。我希望最终结果是整个过程只需要最长的查询(或尽可能接近)。

寻找推荐的方法来结束一些真正意义上的异步填充DataTable的方法。

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

回答

5

我会建议有一个单独的工作线程为每个。你可以使用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()); 
+0

所以一旦我每次排队一升上来,我怎么能等到这两个已完成?我需要两个表填充,然后才能继续并处理它们 – AdaTheDev 2010-01-21 12:04:20

+0

我已将等待概念添加到我的答案中,如果有帮助的话。 – 2010-01-21 12:05:30

+0

@AdaTheDev,你可以使用AutoResetEvents,在工作完成之后(在每个单独的线程中)你会触发它。请参阅@Neils答案,因为他已经提供了一个示例。 – James 2010-01-21 12:08:37

1

这是因为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); 
} 
+0

谢谢(都)。我在STA线程上运行,这意味着我不能使用WaitHandle.WaitAll(“WaitAll在STA线程上不支持多个句柄”)。有其他选择吗? – AdaTheDev 2010-01-21 13:50:43

+0

你可以提供一个'Action'回调函数,当它被调用足够的次数时,它只会执行下一个代码段?不是很好,但应该工作。 – 2010-01-21 15:29:06

+0

为建议和帮助而欢呼。我得到了足够的工作去做我想做的事情(没有真正的好处并行运行查询,然后彼此竞争)。 +1的答案,因为它确实有帮助。 – AdaTheDev 2010-01-23 12:29:07