C# 多个 BackgroundWorker

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10694271/
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-08-09 14:50:50  来源:igfitidea点击:

C# Multiple BackgroundWorkers

c#backgroundworker

提问by Jay Webster

I am trying to setup multiple BackgroundWorkers to do work and when not busy start doing the next bit of work. I can't seem to get them working properly. I have the below code.

我正在尝试设置多个BackgroundWorkers 来完成工作,当不忙时开始做下一点工作。我似乎无法让它们正常工作。我有以下代码。

When I set FilesToProcessequal or less than MaxThreadsit works perfectly although if I make it higher, the app freezes.

当我设置FilesToProcess等于或小于MaxThreads它时,它可以完美运行,但如果我将它设置得更高,应用程序就会冻结。

I am sure it is something simple, but I just can't see it.

我敢肯定它很简单,但我就是看不到它。

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace bgwtest
{
    public partial class Form1 : Form
    {
        private const int MaxThreads = 20;
        private const int FilesToProcess = 21;
        private BackgroundWorker[] threadArray = new BackgroundWorker[MaxThreads];

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1Load(object sender, EventArgs e)
        {
            InitializeBackgoundWorkers();
        }

        private void InitializeBackgoundWorkers()
        {
            for (var f = 0; f < MaxThreads; f++)
            {
                threadArray[f] = new BackgroundWorker();
                threadArray[f].DoWork += new DoWorkEventHandler(BackgroundWorkerFilesDoWork);
                threadArray[f].RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerFilesRunWorkerCompleted);
                threadArray[f].WorkerReportsProgress = true;
                threadArray[f].WorkerSupportsCancellation = true;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (var f = 0; f < FilesToProcess; f++)
            {
                var fileProcessed = false;
                while (!fileProcessed)
                {
                    for (var threadNum = 0; threadNum < MaxThreads; threadNum++)
                    {
                        if (!threadArray[threadNum].IsBusy)
                        {
                            Console.WriteLine("Starting Thread: {0}", threadNum);

                            threadArray[threadNum].RunWorkerAsync(f);
                            fileProcessed = true;
                            break;
                        }
                    }
                    if (!fileProcessed)
                    {
                        Thread.Sleep(50);
                    }
                }
            }
        }

        private void BackgroundWorkerFilesDoWork(object sender, DoWorkEventArgs e)
        {
            ProcessFile((int)e.Argument);

            e.Result = (int)e.Argument;
        }

        private static void ProcessFile(int file)
        {
            Console.WriteLine("Processing File: {0}", file);
        }

        private void BackgroundWorkerFilesRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }

            Console.WriteLine("Processed File: {0}", (int)e.Result);
        }
    }
}

采纳答案by Ethan Brown

The problem appears to be that your workers are never completing. Why this is, I'm not sure; it has something to do with the fact that the method (and thread) you are running them from is not itself completing. I was able to solve the problem by creating another worker to assign files to the worker array:

问题似乎是您的工人永远不会完成。为什么会这样,我不确定;它与您运行它们的方法(和线程)本身并未完成这一事实有关。我能够通过创建另一个工作人员将文件分配给工作人员数组来解决问题:

    private BackgroundWorker assignmentWorker;

    private void InitializeBackgoundWorkers() {
        assignmentWorker = new BackgroundWorker();
        assignmentWorker.DoWork += AssignmentWorkerOnDoWork;
        // ...
    }

    private void AssignmentWorkerOnDoWork( object sender, DoWorkEventArgs doWorkEventArgs ) {
        for( var f = 0; f < FilesToProcess; f++ ) {
            var fileProcessed = false;
            while( !fileProcessed ) {
                for( var threadNum = 0; threadNum < MaxThreads; threadNum++ ) {
                    if( !threadArray[threadNum].IsBusy ) {
                        Console.WriteLine( "Starting Thread: {0}", threadNum );

                        threadArray[threadNum].RunWorkerAsync( f );
                        fileProcessed = true;
                        break;
                    }
                }
                if( !fileProcessed ) {
                    Thread.Sleep( 50 );
                    break;
                }
            }
        }
    }

    private void button1_Click( object sender, EventArgs e ) {
        assignmentWorker.RunWorkerAsync();
    }

I'm not happy with this answer because I don't know why, exactly, it didn't work as you originally designed it. Perhaps someone else can answer that...? At least this will get you a working version.

我对这个答案不满意,因为我不知道为什么,确切地说,它不像你最初设计的那样工作。也许其他人可以回答那个......?至少这会给你一个工作版本。

EDIT: Your original version didn't work because the BackgroundWorkerFilesRunWorkerCompletedruns on the same thread as button1_Click(the UI thread). Since you are not freeing up the UI thread, the thread is never getting marked as complete.

编辑:您的原始版本不起作用,因为BackgroundWorkerFilesRunWorkerCompleted它与button1_Click(UI 线程)在同一线程上运行。由于您没有释放 UI 线程,因此该线程永远不会被标记为完成。

回答by Martin Verjans

In completion to Ethan Brown's answer, the problem effectively comes from the fact that you are running a forever busy loop inside the UI thread.

在完成 Ethan Brown 的回答之后,问题实际上来自这样一个事实,即您在 UI 线程内运行一个永远繁忙的循环。

If filesToProcessis lower than the max number of threads, the loop will assign each file to one thread and exit. No problems there.

如果filesToProcess小于最大线程数,循环会将每个文件分配给一个线程并退出。没有问题。

But if you have more files than threads, then look at what happens when you try process the file while all threads are already running :

但是,如果文件多于线程,那么看看在所有线程都已经运行的情况下尝试处理文件时会发生什么:

  • you check if all threads are busy, yes they are.
  • You wait by doing a thread sleep.
  • 你检查所有线程是否都忙,是的。
  • 你通过做一个线程睡眠来等待。

The problem is that when a background worker completes, it will post a message into the UI Thread message queue. And your UI thread is actually busy checking your threads and waiting.

问题在于,当后台工作者完成时,它会将消息发布到 UI 线程消息队列中。而您的 UI 线程实际上正忙于检查您的线程并等待。

To solve this :

要解决这个问题:

  • Either you put the loop in another thread (as Ethan Brown's solution)
  • Eihter you add Application.DoEvents()before or after the Thread.Sleep() in order to force your UI thread to process the messages. It is not really good practice, but illustrates very well the problem here.
  • 要么将循环放在另一个线程中(如 Ethan Brown 的解决方案)
  • 您可以Application.DoEvents()在 Thread.Sleep() 之前或之后添加以强制您的 UI 线程处理消息。这不是一个很好的实践,但很好地说明了这里的问题。