2010-01-21 11 views
8

Korzystanie z C#/.NET 3.5.SqlDataAdapter.Fill - Asynchronous approach

Obecnie zapełniam 2 DataTables jeden po drugim za pomocą SqlDataAdapter.Fill().

Chcę zapełnić oba te DataTables równolegle, w tym samym czasie, wykonując każdą asynchronicznie. Jednak nie ma asynchronicznej wersji metody Fill() - tzn. BeginFill() byłoby świetne!

Jedno podejście Próbowałem to (pseudo):

  1. SqlCommand1.BeginExecuteReader // 1-ty kwerendy dla DataTable1
  2. SqlCommand2.BeginExecuteReader // 2-ty kwerendy dla DataTable2
  3. SqlCommand1.EndExecuteReader
  4. SqlCommand2.EndExecuteReader
  5. DataTable1.Load (DataReader1)
  6. DataTable2.Load (DataReader2)

Jednakże DataTable.Load() trwa długo:
trwa 3 sekundy wykonać etap 1 do etapu 4.
kroku 5 następuje 22 sekund.
Krok 6 trwa 17 sekund.
Więc, w połączeniu 39 sekund na kroki 5 i 6.

Efektem końcowym jest, to daje mi żadnych korzyści nad prostu robi 2 SqlDataAdapter.Fills, jedną po drugiej. Chcę, aby wynik netto był taki, że cały proces trwa tylko tak długo, jak najdłuższe zapytanie (lub tak blisko jak to możliwe).

Poszukuje rekomendowanych sposobów, aby zakończyć się czymś, co jest naprawdę asynchronicznym podejściem do wypełniania DataTable.

Czy mogę po prostu samodzielnie zarządzać i przetasować 2 oddzielne wątki, z których każdy wypełnia DataTable?

Odpowiedz

5

Proponuję dla każdego z nich osobny wątek roboczy. Możesz użyć 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

Więc raz ja w kolejce każdy jeden w górę, w jaki sposób mogę się doczekać, aż oba ukończyłeś? Potrzebuję obu tabel wypełnionych, zanim będę mógł je kontynuować i przetworzyć. – AdaTheDev

+0

Dodałem koncepcję oczekiwania do mojej odpowiedzi, jeśli to pomaga. –

+0

@AdaTheDev, użyłbyś AutoResetEvents, który wyzwoliłbyś po zakończeniu pracy (wewnątrz każdego osobnego wątku). Zobacz odpowiedź @Neils, ponieważ podał już przykład. – James

1

To dlatego, że DataTable posiada wiele obiektów do tworzenia (wiersze, wartości). Powinieneś mieć wykonanie adaptera i populację datatable wszystko zrobione w innym wątku i zsynchronizować czekając na zakończenie każdej operacji przed kontynuowaniem.

Poniższy kod został napisany w Notatniku i prawdopodobnie nawet nie skompilować, ale mam nadzieję, że masz pomysł ...

// 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

Dzięki (oba). Używam wątku STA, co oznacza, że ​​nie mogę używać WaitHandle.WaitAll ("WaitAll dla wielu uchwytów na wątku STA nie jest obsługiwany"). Czy istnieje alternatywa? – AdaTheDev

+0

Możesz podać callback 'Action', który wykonuje tylko trochę kodu, jeśli został wystarczająco wcześnie wywołany? Niezbyt przyjemne, ale powinno działać. –

+0

Pozdrowienia za sugestie i pomoc. Mam wystarczająco dużo pracy, aby zrobić to, co chciałem (nie było żadnej realnej korzyści z równoległego prowadzenia zapytań, jak wtedy rywalizowali ze sobą). +1 za odpowiedź, ponieważ pomogło. – AdaTheDev

Powiązane problemy