BackgroundWorkers никогда не перестают быть занятыми - PullRequest
9 голосов
/ 02 февраля 2010
for (do it a bunch of times)
{         
    while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy &&
           backgroundWorker3.IsBusy && backgroundWorker4.IsBusy &&
           backgroundWorker5.IsBusy)
    {
        System.Threading.Thread.Sleep(0001);
    }

    if (!backgroundWorker1.IsBusy)
    {
        backgroundWorker1.RunWorkerAsync();
    }
    else if (!backgroundWorker2.IsBusy)
    {
        backgroundWorker2.RunWorkerAsync();
    }
    else if (!backgroundWorker3.IsBusy)
    {
        backgroundWorker3.RunWorkerAsync();
    }
    else if (!backgroundWorker4.IsBusy)
    {
        backgroundWorker4.RunWorkerAsync();
    }
    else if (!backgroundWorker5.IsBusy)
    {
        backgroundWorker5.RunWorkerAsync();
    }
}

он запускается пять раз (каждый BG-работник один раз) и застревает. Разве фоновые работники не перестают быть занятыми? как проверить доступность?

примечание: есть 5 рабочих потоков, это гарантирует, что ни один из них никогда не останавливается, всегда назначая им работу. Но они отказываются сообщать мне, когда они доступны, я подумал, что было бы простое решение ..

- [запрос на редактирование] ---

На самом деле это был всего лишь фиктивный параметр, я удалил его и забыл вытащить его, я использую его только для вызова придворного, который выполняет грязную работу:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    timeconsumingfunction(publicstring);
}

И функция, отнимающая много времени, заканчивается. входя в него в отладчике и выполняя строку за строкой, он доходит до конца и попадает в финальный символ '}'. Это означает, что это заканчивается, верно?

--- [РЕДАКТИРОВАТЬ ОТВЕТ] ---- это сработало просто заменив строку

System.Threading.Thread.Sleep(0001);

с

Application.DoEvents();

Полагаю, он запустит фон, но не получит ответ и не обновит теги IsBusy.

Спасибо всем, отличные ответы, очень помогли!

Ответы [ 8 ]

34 голосов
/ 02 февраля 2010

Ваш цикл вызывает тупик, BGW не могут завершиться. Проблема заключается в событии RunWorkerCompleted, которое возникает в потоке пользовательского интерфейса. Этот бит магии BGW требует, чтобы поток пользовательского интерфейса был бездействующим, он должен прокачать цикл сообщений. Проблема в том, что поток пользовательского интерфейса не простаивает и не перекачивает сообщения, он застрял в цикле for. Таким образом, обработчик событий не может быть запущен, а IsBusy остается истинным.

Вам нужно будет сделать это по-другому. Используйте событие RunWorkerCompleted для запуска кода, который вы обычно запускаете после цикла for. Не поддавайтесь искушению вызвать Application.DoEvents () внутри цикла.

3 голосов
/ 02 февраля 2010

Я предлагаю вам изменить код для обработки события RunWorkerCompleted, чтобы получать уведомления, когда ваши BackgroundWorker закончили свою работу. Есть пример использования BackgroundWorker в официальной документации .

2 голосов
/ 02 февраля 2010

У меня была такая же проблема при использовании фоновых рабочих, и я пришел к выводу, что если вы будете использовать sleep () внутри цикла, то он застрянет. Вы можете использовать событие RunWorkerCompleted и установить логический флаг, чтобы указать, когда каждый работник закончил.

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

1 голос
/ 11 мая 2010

У меня была похожая проблема, и я решил ее заключить в то, чтобы заключить в основную функцию оператор try... catch... and finally....

1 голос
/ 02 февраля 2010

Ваш основной поток должен перекачивать сообщения Windows (либо вызывая Application.DoEvents в вашем цикле while, либо, что еще лучше, используя вместо цикла цикл Systems.Windows.Forms.Timer).

Если выне перекачивайте сообщения Windows, уведомления «завершенных» вашего фонового сотрудника не будут обрабатываться, поэтому статус будет оставаться занятым.

0 голосов
/ 11 января 2013

Рабочее решение вашей проблемы приведено в примере ниже. Как объяснил Ханс Пассант, ваш код выполняется в фоновом режиме, но RunWorkerCompleted каждого потока оценивается только в потоке пользовательского интерфейса. Для этого вы можете поставить запрос в очередь потоков пользовательского интерфейса ThreadPool. Таким образом, пользовательский интерфейс оценивает RunWorkerCompletedEvent и после этого возвращается к вашему коду.

for (int i = 0; i < 999999; ++i)
{
    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback((x) =>
    {
        while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy)
        {
            System.Threading.Thread.Sleep(0);
        }
        // Code that is beging executed after all threads have ended. 
        myCode();
    }));

    if (!backgroundWorker1.IsBusy)
    {
        backgroundWorker1.RunWorkerAsync();
    }
    else if (!backgroundWorker2.IsBusy)
    {
        backgroundWorker2.RunWorkerAsync();
    }        
}
0 голосов
/ 02 февраля 2010

Проблема в том, что все, что вы делаете в worker.RunWorkerAsync(), никогда не закончится.Возможно, в вашем событии DoWork определен какой-то бесконечный цикл или что-то подобное.

Вот рабочий пример, который выбирает следующий свободный работник:

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

namespace ConsoleApplication1
{
    class Program
    {
        private static List<MyWorker> _Workers;

        static void Main(string[] args)
        {
            _Workers = new List<MyWorker>();

            for (int i = 0; i < 5; i++)
            {
                _Workers.Add(CreateDefaultWorker(i));
            }

            StartJobs(20000);
            Console.ReadKey();
        }

        private static void StartJobs(int runtime)
        {
            Random rand = new Random();
            DateTime startTime = DateTime.Now;

            while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(runtime))
            {
                var freeWorker = GetFreeWorker();

                if (freeWorker != null)
                {
                    freeWorker.Worker.RunWorkerAsync(new Action(() => DoSomething(freeWorker.Index, rand.Next(500, 2000))));
                }
                else
                {
                    Console.WriteLine("No free worker available!");
                    Console.WriteLine("Waiting for free one...");
                    WaitForFreeOne();
                }
            }
        }

        private static MyWorker GetFreeWorker()
        {
            foreach (var worker in _Workers)
            {
                if (!worker.Worker.IsBusy)
                    return worker;
            }

            return null;
        }

        private static void WaitForFreeOne()
        {
            while (true)
            {
                foreach (var worker in _Workers)
                {
                    if (!worker.Worker.IsBusy)
                        return;
                }
                Thread.Sleep(1);
            }
        }

        private static MyWorker CreateDefaultWorker(int index)
        {
            var worker = new MyWorker(index);

            worker.Worker.DoWork += (sender, e) => ((Action)e.Argument).Invoke();
            worker.Worker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Job finished in worker " + worker.Index);

            return worker;
        }

        static void DoSomething(int index, int timeout)
        {
            Console.WriteLine("Worker {1} starts to work for {0} ms", timeout, index);
            Thread.Sleep(timeout);
        }
    }

    public class MyWorker
    {
        public int Index { get; private set; }
        public BackgroundWorker Worker { get; private set; }

        public MyWorker(int index)
        {
            Index = index;
            Worker = new BackgroundWorker();
        }
    }
}
0 голосов
/ 02 февраля 2010

.IsBusy указывает только на то, что фоновый работник действительно выполняет операцию. Он будет занят, пока «что-то» не будет завершено. Кажется, что «что-то» не заканчивается, и ваши фоновые работники заняты.

Так что было бы полезно, если бы вы могли объяснить, что такое «что-то», и, возможно, сколько времени потребуется для выполнения «чего-то» в основном потоке.

...