Используя.NET класс BackgroundWorker в консольном приложении

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

В моем случае в основном у меня есть класс контроллера, где я хочу метать икру от нескольких рабочих потоков, чтобы сделать некоторую обработку (ограничивающий макс. количество рабочих потоков, порожденных с помощью семафора). Тогда я хочу, чтобы мой класс контроллера заблокировался, пока все потоки не завершили обработку. Таким образом после того, как я запускаю рабочий поток, чтобы сделать некоторую работу, я хочу, чтобы поток был в состоянии уведомить поток контроллера, когда обработка завершена. Я вижу, что могу использовать фоновый класс рабочего, и обрабатывать события DoWork и RunWorkerCompleted для выполнения этого, однако задавался вопросом, является ли это хорошей идеей? Там лучшие пути состоят в том, чтобы достигнуть этого?

12
задан jvtech 3 February 2010 в 23:06
поделиться

2 ответа

Это не будет работать в консольном приложении, поскольку SynchronizationContext.Current никогда не будет инициализирован. Это инициализируется Windows Forms или WPF для вас, когда вы используете приложение с графическим интерфейсом.

При этом нет причин для этого. Просто используйте ThreadPool.QueueUserWorkItem и событие сброса ( ManualResetEvent или AutoResetEvent ), чтобы уловить состояние завершения и заблокировать ваш основной поток.


Изменить:

Увидев некоторые комментарии OP, я подумал, что добавлю это.

На мой взгляд, «самой хорошей» альтернативой было бы получить копию Rx Framework , поскольку она включает в себя обратный порт TPL в .NET 4. Это позволит вам использовать перегрузка Parallel.ForEach , которая предоставляет возможность предоставления экземпляра ParallelOptions . Это позволит вам ограничить общее количество одновременных операций и выполнить всю работу за вас:

// using collection of work items, such as: List<Action<object>> workItems;
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = 10; // Restrict to 10 threads, not recommended!

// Perform all actions in the list, in parallel
Parallel.ForEach(workItems, options, item => { item(null); });

Однако, используя Parallel.ForEach, я лично позволил бы системе управлять степенью параллелизма. Он автоматически назначит соответствующее количество потоков (особенно, когда / если это переходит на .NET 4).

8
ответ дан 2 December 2019 в 19:31
поделиться

Если y наше требование - просто блокировать, пока не завершатся все потоки, это действительно просто - просто запустите новые потоки и затем вызовите Thread.Join для каждого из них:

using System;
using System.Collections.Generic;
using System.Threading;

public class Test
{
    static void Main()
    {
        var threads = new List<Thread>();
        for (int i = 0; i < 10; i++)
        {
            int copy = i;
            Thread thread = new Thread(() => DoWork(copy));
            thread.Start();
            threads.Add(thread);
        }

        Console.WriteLine("Main thread blocking");
        foreach (Thread thread in threads)
        {
            thread.Join();
        }
        Console.WriteLine("Main thread finished");
    }

    static void DoWork(int thread)
    {
        Console.WriteLine("Thread {0} doing work", thread);
        Random rng = new Random(thread); // Seed with unique numbers
        Thread.Sleep(rng.Next(2000));
        Console.WriteLine("Thread {0} done", thread);
    }
}

EDIT: если у вас есть доступ к .NET 4.0, то TPL определенно правильный путь.В противном случае я бы предложил использовать очередь производителя / потребителя (вокруг много примеров кода). В основном у вас есть очередь рабочих элементов и столько потребительских потоков, сколько у вас ядер (при условии, что они привязаны к ЦП; вы бы хотели адаптировать его к своей рабочей нагрузке). Каждый потребительский поток будет брать элементы из очереди и обрабатывать их по одному. То, как именно вы это сделаете, будет зависеть от вашей ситуации, но это не так уж сложно. Еще проще, если вы можете выполнить всю работу, которую вам нужно выполнить для начала, чтобы потоки могли просто выйти, когда обнаружат, что очередь пуста.

9
ответ дан 2 December 2019 в 19:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: