Подтвердить что ты не робот

Создайте несколько потоков для работы, затем подождите, пока все закончится

просто хочу получить рекомендации по "лучшей практике" в отношении многопоточных задач.

в качестве примера у нас есть приложение С#, которое при запуске считывает данные из таблицы "type" в нашей базе данных и сохраняет информацию в коллекции, которую мы передаем в приложении. это мешает нам ударять базу данных каждый раз, когда эта информация требуется.

в настоящее время приложение считывает данные из 10 таблиц синхронно. я действительно хотел бы, чтобы приложение читалось из каждой таблицы в другом потоке, все работало параллельно. приложение будет ждать завершения всех потоков, прежде чем продолжить запуск приложения.

Я просмотрел BackGroundWorker, но просто хочу получить совет по выполнению вышеуказанного.

  • Этот метод звучит логично, чтобы ускорить время запуска нашего приложения.
  • Как лучше всего обрабатывать все потоки, имея в виду, что каждая работа нитей не зависит друг от друга, нам просто нужно дождаться завершения всех потоков, прежде чем продолжить.

Я с нетерпением жду некоторых ответов

4b9b3361

Ответ 1

Мое предпочтение в этом заключается в том, чтобы обрабатывать это через один WaitHandle и использовать блокировку, чтобы избежать блокировки на счетчике:

class Program
{
    static void Main(string[] args)
    {
        int numThreads = 10;
        ManualResetEvent resetEvent = new ManualResetEvent(false);
        int toProcess = numThreads;

        // Start workers.
        for (int i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // If we're the last thread, signal
                if (Interlocked.Decrement(ref toProcess) == 0)
                    resetEvent.Set();
            }).Start();
        }

        // Wait for workers.
        resetEvent.WaitOne();
        Console.WriteLine("Finished.");
    }
}

Это хорошо работает и масштабируется до любого количества обработки потоков, без введения блокировки.

Ответ 2

Мне нравится решение @Reed. Другой способ сделать то же самое в .NET 4.0 - использовать CountdownEvent.

class Program
{
    static void Main(string[] args)
    {
        var numThreads = 10;
        var countdownEvent = new CountdownEvent(numThreads);

        // Start workers.
        for (var i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // Signal the CountdownEvent.
                countdownEvent.Signal();
            }).Start();
        }

        // Wait for workers.
        countdownEvent.Wait();
        Console.WriteLine("Finished.");
    }
}

Ответ 3

Если у вас более 64 команд ожидания для потока STA, как говорит Марк. вы можете создать список с вашими потоками и ждать, пока все закончится во втором цикле.

//check that all threads have completed.
foreach (Thread thread in threadList)
{
     thread.Join();

}  

Ответ 4

Если вы не используете .NET 4.0, вы можете использовать List < ManualResetEvent > , по одному для каждого потока и Подождите, чтобы они были Установить. Чтобы подождать несколько потоков, вы можете использовать WaitAll, но следите за лимитом 64 команд ожидания. Если вам нужно больше этого, вы можете просто зациклиться на них и ждать каждого отдельно.

Если вам нужен более быстрый запуск, вам, вероятно, не нужно ждать, пока все данные будут прочитаны во время запуска. Просто покажите графический интерфейс, и любая информация, которая отсутствует, может быть выделена серым цветом с помощью какого-либо значка "Обновление..." или аналогичного. Когда информация входит, просто запустите событие для обновления GUI. Может быть много операций, которые пользователь может начать выполнять даже до того, как будут прочитаны все данные из всех таблиц.

Ответ 5

Если вы чувствуете себя авантюристом, вы можете использовать С# 4.0 и параллельную библиотеку задач:

Parallel.ForEach(jobList, curJob => {
  curJob.Process()
});

Ответ 6

Просто для удовольствия, что сделал @Reed, с монитором.: P

class Program
{
    static void Main(string[] args)
    {
        int numThreads = 10;
        int toProcess = numThreads;
        object syncRoot = new object();

        // Start workers.
        for (int i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // If we're the last thread, signal
                if (Interlocked.Decrement(ref toProcess) == 0)
                {
                    lock (syncRoot)
                    {
                        Monitor.Pulse(syncRoot);
                    }
                }
            }).Start();
        }

        // Wait for workers.
        lock (syncRoot)
        {
            if (toProcess > 0)
            {
                Monitor.Wait(syncRoot);
            }
        }

        Console.WriteLine("Finished.");
    }
}

Ответ 7

Вот два шаблона для ожидания нескольких параллельных операций. Хитрость заключается в том, что вы должны лечить свой основной поток, как если бы это была одна из параллельных операций. В противном случае между сигналами завершения в рабочих потоках и ожиданием этого сигнала из основного потока существует тонкое условие гонки.

int threadCount = 1;
ManualResetEvent finished = new ManualResetEvent(false);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  Interlocked.Increment(ref threadCount); 
  ThreadPool.QueueUserWorkItem(delegate 
  { 
      try 
      { 
           // do work 
      } 
      finally 
      { 
          if (Interlocked.Decrement(ref threadCount) == 0) finished.Set();
      } 
  }); 
}
if (Interlocked.Decrement(ref threadCount) == 0) finished.Set();
finished.WaitOne(); 

В качестве личного предпочтения мне нравится использовать CountdownEvent класс для подсчета для меня, который доступен в .NET 4.0.

var finished = new CountdownEvent(1);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  finished.AddCount();
  ThreadPool.QueueUserWorkItem(delegate 
  { 
      try 
      { 
           // do work 
      } 
      finally 
      { 
        finished.Signal();
      } 
  }); 
}
finished.Signal();
finished.Wait(); 

В приведенных выше примерах используется ThreadPool, но вы можете поменять его на любой механизм нитей, который вы предпочитаете.

Ответ 8

Другая возможность с TPL, предполагая, что jobs - это коллекции элементов для обработки или подпрограммы для запуска:

Task.WaitAll(jobs
    .Select(job => TaskFactory.StartNew(() => /*run job*/))
    .ToArray());

Ответ 9

Предполагая, что потоки чтения базы данных возвращаются, как только они будут завершены, вы можете просто вызвать Thread.Join для всех десяти потоков, в свою очередь, из инициирующего потока.

Ответ 10

Если вы используете .NET 3.5 или ниже, вы можете использовать массив AsyncResult или BackgroundWorker и подсчитать, сколько потоков было возвращено (просто не забудьте уменьшить счетчик с помощью блокированных операций) (см. http://www.albahari.com/threading/ в качестве ссылки).

Если вы используете .NET 4.0, это самый простой подход.

Ответ 11

более простой метод, который мне нравится использовать:

    private int ThreadsCount = 100; //initialize threads count
    private void button1_Click(object sender, EventArgs e)
    {   
        for (int i = 0; i < ThreadsCount; i++)
        {
            Thread t = new Thread(new ThreadStart(myMethod));
            t.IsBackground = true;
            t.Start(); 
        } 
    }

    private void myMethod()
    {
        //everytime a thread finishes executing decrease the threads count
        ThreadsCount = ThreadsCount - 1;

        if (ThreadsCount < 1)
        {
            //if all threads finished executing do whatever you wanna do here..
            MessageBox.Show("Finished Executing all threads!!!");
        }
    }

Ответ 12

Проводя, возможно, помогая некоторым другим, потратил немало времени на поиск решения, как то, что я придумал. Поэтому я принял несколько иной подход. Я откручивал множество потоков и увеличивал счетчик и уменьшал счетчик, когда поток начинался и останавливался. Затем в основном методе, который я хотел сделать паузой и ждать завершения потоков, я сделал.

while (threadCounter > 0)
{
    Thread.Sleep(500); //Make it pause for half second so that we don’t spin the cpu out of control.
}

Документировано в моем блоге. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/