visual-studio 背景工作者永远不会停止忙碌

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2183520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 11:30:04  来源:igfitidea点击:

BackgroundWorkers never stop being busy

c#.netvisual-studiomultithreadingbackgroundworker

提问by Marcelo

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();
    }
}

it runs five times (every BG-worker once) and gets stuck in the while. Don't the backgroundworkers ever stop being busy ? how do I check availability ?

它运行了五次(每个 BG-worker 一次)并且卡在了其中。后台工作人员永远不会停止忙碌吗?我如何检查可用性?

note: there are 5 worker threads, this assures none of them is ever stopped, always assigning work to them. But they refuse to tell me when they are available, I thought that would have a simple solution..

注意:有 5 个工作线程,这确保它们都不会停止,始终为它们分配工作。但是他们拒绝告诉我什么时候有空,我认为那会有一个简单的解决方案..

--[edit request]---

--[编辑请求]---

Actually it was only a dummy parameter, i removed it and forgot to get it out, I only use it to call the dowork, who does the dirty job:

实际上它只是一个虚拟参数,我删除了它忘记取出它,我只用它来调用dowork,谁做肮脏的工作:

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

And the timeconsumingfunction DOES end. stepping into it in the debugger and running line per line, it goes until the end and arrives in the final '}'. That means it ends, right ?

而耗时的功能确实结束了。在调试器中进入它并每行运行一行,它一直持续到最后并到达最后的'}'。这意味着它结束了,对吧?

---[EDIT ANSWER]---- it worked by JUST replacing the line

---[编辑答案]---- 它只是通过更换线路来工作

System.Threading.Thread.Sleep(0001);

with

Application.DoEvents();

I guess it would run the background, but not receive the answer and not update the IsBusy tags.

我猜它会运行后台,但不会收到答案,也不会更新 IsBusy 标签。

Thanks everybody, great answers, helped a lot!

谢谢大家,很好的答案,帮助很大!

回答by Hans Passant

Your loop is causing deadlock, the BGWs cannot complete. The problem is the RunWorkerCompleted event, it is raised on the UI thread. That bit of BGW magic requires the UI thread to be idle, it must be pumping its message loop. Problem is, the UI thread is not idle and it isn't pumping messages, it is stuck in the for loop. Thus, the event handler cannot run and IsBusy stays true.

您的循环导致死锁,BGW 无法完成。问题是 RunWorkerCompleted 事件,它是在 UI 线程上引发的。那个 BGW 魔法要求 UI 线程处于空闲状态,它必须泵送其消息循环。问题是,UI 线程不是空闲的,也不是泵送消息,而是卡在 for 循环中。因此,事件处理程序无法运行并且 IsBusy 保持为真。

You'll need to do this differently. Leverage the RunWorkerCompleted event to run the code that you'd normally run after this for loop. Resist all temptation to call Application.DoEvents() inside the loop.

您需要以不同的方式执行此操作。利用 RunWorkerCompleted 事件运行您通常在此 for 循环之后运行的代码。抵制在循环内调用 Application.DoEvents() 的所有诱惑。

回答by Anders Fjeldstad

I suggest you change your code to handle the RunWorkerCompletedevent to get notifications when your BackgroundWorkers are finished with their work. There is an example of how to use the BackgroundWorkerin the official documentation.

我建议您更改代码以处理RunWorkerCompleted事件,以便在您的BackgroundWorkers 完成工作时收到通知。还有就是如何使用一个例子BackgroundWorker的官方文档

回答by 53an

I had this same problem whilst using background workers and came the conclusion that if you use sleep() within a loop, then it will get stuck. You can either use the RunWorkerCompleted event and set a boolean flag to indicate when each worker is finished.

我在使用后台工作人员时遇到了同样的问题,得出的结论是,如果您在循环中使用 sleep(),那么它会卡住。您可以使用 RunWorkerCompleted 事件并设置一个布尔标志来指示每个工作程序何时完成。

Or if you want to abort the thread regardless you could look at using threads instead of background workers. However then you lose the ease of use in regards to the events which the background worker provides.

或者,如果您想中止线程,无论您可以考虑使用线程而不是后台工作者。但是,您将失去后台工作人员提供的事件的易用性。

回答by Joe

Your main thread needs to be pumping Windows messages (either calling Application.DoEvents in your while loop, or better still by using a Systems.Windows.Forms.Timer instead of a loop).

您的主线程需要泵送 Windows 消息(在您的 while 循环中调用 Application.DoEvents,或者最好使用 Systems.Windows.Forms.Timer 而不是循环)。

If you don't pump Windows messages, your background worker's "completed" notifications won't be processed, so the status will remain busy.

如果您不发送 Windows 消息,则不会处理后台工作人员的“已完成”通知,因此状态将保持忙碌。

回答by CP Net

I had a similar problem, and what I did to solve it was to enclose the main function with a try... catch... and finally...statement.

我有一个类似的问题,我为解决它所做的是用一个try... catch... and finally...语句将 main 函数括起来。

回答by Oliver

The problem is that whatever you're doing within worker.RunWorkerAsync()never gets finished. Maybe there is some endless loop or something like that defined in your DoWorkevent.

问题是,无论你在里面做什么,都worker.RunWorkerAsync()永远不会完成。也许在您的DoWork事件中定义了一些无限循环或类似的东西。

Here is a working example, that selects the next free worker:

这是一个工作示例,它选择下一个空闲工人:

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();
        }
    }
}

回答by Onots

The .IsBusy only indicates that the backgroundworker is actually performing an operation. It will be busy until "something" is completed. It seems that "something" doesn't finish, keeping your backgroundworkers busy.

.IsBusy 仅表示后台工作人员实际上正在执行操作。它会很忙,直到“某事”完成。似乎“某事”没有完成,让您的后台工作人员忙碌。

So it would help if you could explain what "something" is, and possibly how long it takes to perform "something" on the main thread.

因此,如果您能解释“某事”是什么,以及在主线程上执行“某事”可能需要多长时间,那将会有所帮助。

回答by codingdave

A working solution for your problem is given in the example below. Like Hans Passant explained your code runs in the backgroundworker but the RunWorkerCompleted of each thread is only evaluated in the ui Thread. To do this you can enqueue your request in the UIs queue of threads, the ThreadPool. This way the UI evaluates the RunWorkerCompletedEvent and after that jumps back to your code.

下面的示例中给出了您的问题的可行解决方案。就像 Hans Passant 解释的那样,您的代码在后台工作器中运行,但每个线程的 RunWorkerCompleted 仅在 ui 线程中进行评估。为此,您可以将您的请求放入线程的 UI 队列 ThreadPool 中。这样 UI 会评估 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();
    }        
}