2010-01-21 50 views
8

Utilizando C#/.NET 3.5.SqlDataAdapter.Fill - Aproximación asincrónica

Actualmente estoy poblando 2 DataTables uno después de otro utilizando SqlDataAdapter.Fill().

Quiero llenar ambos DataTables en paralelo, al mismo tiempo haciendo cada uno de forma asincrónica. Sin embargo, no hay una versión asíncrona del método Fill(), es decir, BeginFill() sería genial.

Un enfoque que he probado es (pseudo):

  1. SqlCommand1.BeginExecuteReader // primera consulta, por DataTable1
  2. SqlCommand2.BeginExecuteReader // segunda consulta, para DataTable2
  3. SqlCommand1.EndExecuteReader
  4. SqlCommand2.EndExecuteReader
  5. DataTable1.Load (DataReader1)
  6. DataTable2.Load (DataReader2)

Sin embargo, DataTable.Load() toma mucho tiempo:
Se tarda de 3 segundos para hacer el paso 1 al paso 4.
Paso 5 entonces toma 22 segundos.
El paso 6 lleva 17 segundos.
Por lo tanto, combinados 39 segundos para los pasos 5 y 6.

El resultado final es que esto no me da ningún beneficio sobre solo hacer 2 SqlDataAdapter.Fills, uno después del otro. Quiero que el resultado neto sea que todo el proceso toma solo el tiempo que la consulta más larga (o lo más cerca posible).

En busca de formas recomendadas para terminar con algo que es realmente un enfoque asincrónico para llenar un DataTable.

¿O simplemente lo administro yo mismo y enrollo 2 hilos separados, cada uno llenando un DataTable?

Respuesta

5

Sugeriría tener un hilo de trabajo por separado para cada uno. Puede usar 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

Así que una vez que he hizo cola cada uno, ¿cómo puedo esperar hasta tanto ¿Han completado? Necesito que se llenen ambas tablas antes de continuar y procesarlas – AdaTheDev

+0

Agregué el concepto de espera a mi respuesta, si eso ayuda. –

+0

@AdaTheDev, utilizaría AutoResetEvents que se activaría una vez que el trabajo se haya completado (dentro de cada subproceso independiente). Vea la respuesta de @Neil como ya ha proporcionado un ejemplo. – James

1

Esto es porque el DataTable tiene una gran cantidad de objetos para crear (filas, valores). Debe realizar la ejecución del adaptador y la población de una tabla de datos en un hilo diferente, y sincronizar esperando a que cada operación finalice antes de continuar.

El siguiente código fue escrito en el Bloc de notas y probablemente ni siquiera se compila, pero esperemos que se entiende la idea ...

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

Gracias (ambos). Me estoy ejecutando en un subproceso STA, lo que significa que no puedo usar WaitHandle.WaitAll ("WaitAll para múltiples identificadores en un subproceso STA no es compatible"). ¿Hay una alternativa? – AdaTheDev

+0

¿Podría proporcionar una devolución de llamada 'Action', que solo hace el siguiente bit de código cuando se ha llamado las veces suficientes? No es muy lindo, pero debería funcionar. –

+0

Saludos por la sugerencia y ayuda. Obtuve suficiente trabajo para hacer lo que quería (no había un beneficio real al ejecutar las consultas en paralelo, ya que entonces disputaban entre ellos). +1 por la respuesta, ya que ayudó. – AdaTheDev

Cuestiones relacionadas