a aplicación C # de subprocesos múltiples con llamadas a la base de datos de SQL Server

Tengo una base de datos de SQL Server con 500,000 registros en la tablamain. También hay otras tres tablas llamadaschild1, child2 ychild3. Las relaciones de muchos a muchos entrechild1, child2, child3 ymain se implementan a través de las tres tablas de relaciones:main_child1_relationship, main_child2_relationship ymain_child3_relationship. Necesito leer los registros enmain, actualizarmain, y también inserte en las tablas de relación nuevas filas, así como inserte nuevos registros en las tablas secundarias. Los registros en las tablas secundarias tienen restricciones de unicidad, por lo que el pseudocódigo para el cálculo real (CalculateDetails) sería algo así como:

for each record in main
{
   find its child1 like qualities
   for each one of its child1 qualities
   {
      find the record in child1 that matches that quality
      if found
      {
          add a record to main_child1_relationship to connect the two records
      }
      else
      {
          create a new record in child1 for the quality mentioned
          add a record to main_child1_relationship to connect the two records
      }
   }
   ...repeat the above for child2
   ...repeat the above for child3 
}

Esto funciona bien como una aplicación de subproceso único. Pero es muy lento. El procesamiento en C # es bastante pesado y toma demasiado tiempo. Quiero convertir esto en una aplicación multiproceso.

¿Cuál es la mejor manera de hacer esto? Estamos utilizando Linq para Sql.

Hasta ahora mi enfoque ha sido crear una nuevaDataContext objeto para cada lote de registros demain y useThreadPool.QueueUserWorkItem para procesarlo. Sin embargo, estos lotes pisan los dedos del otro porque un hilo agrega un registro y luego el siguiente hilo intenta agregar el mismo y ... obtengo todo tipo de bloqueos muertos interesantes de SQL Server.

Aquí está el código:

    int skip = 0;
    List<int> thisBatch;
    Queue<List<int>> allBatches = new Queue<List<int>>();
    do
    {
        thisBatch = allIds
                .Skip(skip)
                .Take(numberOfRecordsToPullFromDBAtATime).ToList();
        allBatches.Enqueue(thisBatch);
        skip += numberOfRecordsToPullFromDBAtATime;

    } while (thisBatch.Count() > 0);

    while (allBatches.Count() > 0)
    {
        RRDataContext rrdc = new RRDataContext();

        var currentBatch = allBatches.Dequeue();
        lock (locker)  
        {
            runningTasks++;
        }
        System.Threading.ThreadPool.QueueUserWorkItem(x =>
                    ProcessBatch(currentBatch, rrdc));

        lock (locker) 
        {
            while (runningTasks > MAX_NUMBER_OF_THREADS)
            {
                 Monitor.Wait(locker);
                 UpdateGUI();
            }
        }
    }

Y aquí está ProcessBatch:

    private static void ProcessBatch( 
        List<int> currentBatch, RRDataContext rrdc)
    {
        var topRecords = GetTopRecords(rrdc, currentBatch);
        CalculateDetails(rrdc, topRecords);
        rrdc.Dispose();

        lock (locker)
        {
            runningTasks--;
            Monitor.Pulse(locker);
        };
    }

    private static List<Record> GetTopRecords(RecipeRelationshipsDataContext rrdc, 
                                              List<int> thisBatch)
    {
        List<Record> topRecords;

        topRecords = rrdc.Records
                    .Where(x => thisBatch.Contains(x.Id))
                    .OrderBy(x => x.OrderByMe).ToList();
        return topRecords;
    }

CalculateDetails se explica mejor por el pseudocódigo en la parte superior.

Creo que debe haber una mejor manera de hacer esto. Por favor ayuda. ¡Muchas gracias

Respuestas a la pregunta(14)

Su respuesta a la pregunta