Справочная информацияРаботники никогда не перестают быть занятыми

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-worker один раз) и на время застревает. Разве фоновые работники никогда не перестают быть занятыми? как я могу проверить наличие?

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

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

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

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

И функция, отнимающая много времени, ДЕЙСТВИТЕЛЬНО заканчивается. входя в него в отладчике и выполняя строку за строкой, он идет до конца и прибывает в финал '}'. Значит, все кончено, правда?

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

System.Threading.Thread.Sleep(0001);

с участием

Application.DoEvents();

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

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


person Marcelo    schedule 02.02.2010    source источник
comment
Самый простой способ определить, завершено ли задание, - это сбросить точку останова на событии RunWorkerCompleted и позволить программе работать. Если вы достигли точки останова, фоновый рабочий завершил работу.   -  person 53an    schedule 02.02.2010


Ответы (8)


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

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

person Hans Passant    schedule 02.02.2010
comment
Это почти как вечное «for», и за ним ничего не бежит. Я проверяю доступность веб-сайта в массиве (используя индекс for), поэтому нет возможности просто продолжить работу с RunWorkerCompleted. Если есть предложения, отредактируйте, пожалуйста, свой ответ .. Я не в курсе :( - person Marcelo; 02.02.2010
comment
И это ДЕЙСТВИТЕЛЬНО завершено. Я следил за ним с отладчиком, и он приходит в} в заданный момент, когда был некоторый сон (0001), что не так много, как во время выполнения, потому что между шагами есть время, поэтому я сплю ( 0001) примерно 20 раз ... затем он запускается снова, доходит до '}', но даже после последнего} doWork BGW не изменяет isBusy на false. - person Marcelo; 02.02.2010
comment
Нет, это не завершено, пока не завершится выполнение события RunWorkerCompleted. Что происходит после завершения обработчика событий DoWork. Обработчик RWC - это проблема, он не может работать, потому что ваш поток пользовательского интерфейса застрял в цикле. Удаление обработчика было бы быстрым решением. - person Hans Passant; 02.02.2010
comment
вы сказали мне сопротивляться включению Application.DoEvents () в цикл ... почему? - person Marcelo; 02.02.2010
comment
Потому что ваша программа выйдет из строя и сгорит, когда пользователь закроет форму во время выполнения вашего цикла. Чтобы решить эту проблему, установите для свойства Enable формы значение false перед входом в цикл. - person Hans Passant; 02.02.2010

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

person Anders Fjeldstad    schedule 02.02.2010

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

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

person 53an    schedule 02.02.2010
comment
Уррх - да не поленитесь - спи () меня тоже укусила - person Mark Mullin; 28.03.2012

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

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

person Joe    schedule 02.02.2010

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

person CP Net    schedule 11.05.2010

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

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

person Onots    schedule 02.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();
        }
    }
}
person Oliver    schedule 02.02.2010

Рабочее решение вашей проблемы показано в примере ниже. Как объяснил Ханс Пассант, ваш код работает в фоновом режиме, но RunWorkerCompleted каждого потока оценивается только в потоке пользовательского интерфейса. Для этого вы можете поставить свой запрос в очередь потоков UIs, 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();
    }        
}
person codingdave    schedule 11.01.2013
comment
Голосование против без объяснения причин бесполезно и не приятно. Этот ответ является правильным ответом на заданный вопрос и показывает, как выйти из тупика, используя как можно больше кода от спрашивающего. - person codingdave; 28.07.2014