2010-01-21 1 views
8

Использование C#/.NET 3.5.SqlDataAdapter.Fill - Асинхронный подход

В настоящее время я заполняю 2 DataTables один за другим, используя SqlDataAdapter.Fill().

Я хочу заполнить обе эти DataTables параллельно, в то же время, делая асинхронно каждый. Тем не менее, нет асинхронной версии метода 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() занимает много времени:
Это занимает 3 секунды, чтобы сделать шаг 1 к шагу 4. Шаг
5 затем занимает 22 секунд.
Шаг 6 занимает 17 секунд.
Итак, в общей сложности 39 секунд для этапов 5 и 6.

Конечный результат: это не дает мне никакой пользы от того, как просто сделать 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

+0

Я добавил концепцию ожидания к моему ответу, если это поможет. –

+0

@AdaTheDev, вы должны использовать AutoResetEvents, который вы запускаете после завершения работы (внутри каждого отдельного потока). См. @Neils ответ, поскольку он уже представил пример. – James

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

+0

Вы можете предоставить обратный вызов «Action», который выполняет только следующий бит кода, когда он был вызван достаточно раз? Не очень приятно, но должно работать. –

+0

Приветствия за предложение и помощь. У меня достаточно работы, чтобы делать то, что я хотел (не было никакой реальной выгоды, когда запросы выполнялись параллельно, а затем обсуждались друг с другом). +1 для ответа, поскольку это помогло. – AdaTheDev