2010-01-21 193 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