C#多个BackgroundWorkers

时间:2012-05-22 00:18:39

标签: c# backgroundworker

我正在尝试设置多个BackgroundWorker来做工作,而不忙的时候开始做下一部分工作。我似乎无法让他们正常工作。我有以下代码。

当我将FilesToProcess设置为等于或小于MaxThreads时,它可以正常工作,但如果我将其设置得更高,则该应用会冻结。

我确信这很简单,但我看不到它。

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

2 个答案:

答案 0 :(得分:9)

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

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

我对这个答案不满意,因为我不知道为什么,它确实不像你最初设计的那样有效。也许其他人可以回答......?至少这将为您提供一个有效的版本。

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

答案 1 :(得分:0)

完成Ethan Brown的回答后,问题实际上出在您在UI线程中运行着一个永远忙碌的循环这一事实上。

如果$start_date = new DateTime('31-03-2019'); $end_date = new DateTime('01-05-2019'); $diff = $start_date->diff($end_date); $months = floor($diff->days / 30); $days = $diff->days % 30; echo "day: " . $days . " month: " . $months . "\n"; 小于最大线程数,则循环会将每个文件分配给一个线程并退出。那里没问题。

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

  • 您检查所有线程是否都忙,是的。
  • 您等待线程睡眠。

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

要解决这个问题:

  • 您可以将循环放入另一个线程中(作为Ethan Brown的解决方案)
  • 在Thread.Sleep()之前或之后添加PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=9 TARGET_PRODUCT=omni_beryllium TARGET_BUILD_VARIANT=userdebug TARGET_BUILD_TYPE=release TARGET_ARCH=arm64 TARGET_ARCH_VARIANT=armv8-a TARGET_CPU_VARIANT=kryo300 TARGET_2ND_ARCH=arm TARGET_2ND_ARCH_VARIANT=armv8-a TARGET_2ND_CPU_VARIANT=cortex-a75 HOST_ARCH=x86_64 HOST_2ND_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=Linux-4.9.0-8-amd64-x86_64-Debian-GNU/Linux-9-(stretch) HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release BUILD_ID=PQ2A.190205.001 OUT_DIR=/home/benjamin/OMNI/out ============================================ ninja: no work to do. ninja: no work to do. wildcard(/home/benjamin/OMNI/out/target/product/beryllium/clean_steps.mk) was changed, regenerating... wildcard(vendor/xiaomi/whyred/Android.mk) was changed, regenerating... [24/971] including development/build/Android.mk ... development/build/build_android_stubs.mk:43: warning: android_stubs_current development/build/build_android_stubs.mk:43: warning: metalava_android_stubs_current metalava_android_stubs_current development/build/build_android_stubs.mk:43: warning: android_system_stubs_current development/build/build_android_stubs.mk:43: warning: android_test_stubs_current development/build/build_android_stubs.mk:43: warning: metalava_android_system_stubs_current metalava_android_system_stubs_current development/build/build_android_stubs.mk:43: warning: metalava_android_test_stubs_current metalava_android_test_stubs_current [539/971] including system/sepolicy/Android.mk ... system/sepolicy/Android.mk:79: warning: BOARD_SEPOLICY_VERS not specified, assuming current platform version [971/971] including vendor/xiaomi/tissot/Android.mk ... build/make/core/base_rules.mk:412: warning: overriding commands for target `/home/benjamin/OMNI/out/target/product/beryllium/root/res/images/charger/battery_fail.png' build/make/core/base_rules.mk:412: warning: ignoring old commands for target `/home/benjamin/OMNI/out/target/product/beryllium/root/res/images/charger/battery_fail.png' build/make/core/base_rules.mk:412: warning: overriding commands for target `/home/benjamin/OMNI/out/target/product/beryllium/root/res/images/charger/battery_scale.png' build/make/core/base_rules.mk:412: warning: ignoring old commands for target `/home/benjamin/OMNI/out/target/product/beryllium/root/res/images/charger/battery_scale.png' build/make/core/Makefile:28: warning: overriding commands for target `/home/benjamin/OMNI/out/target/product/beryllium/system/etc/mkshrc' build/make/core/base_rules.mk:412: warning: ignoring old commands for target `/home/benjamin/OMNI/out/target/product/beryllium/system/etc/mkshrc' ninja: error: '/home/benjamin/OMNI/out/target/common/obj/JAVA_LIBRARIES/WfdCommon_intermediates/classes.jar', needed by '/home/benjamin/OMNI/out/target/common/obj/PACKAGING/boot-jars-package-check_intermediates/stamp', missing and no known rule to make it 14:54:42 ninja failed with: exit status 1 #### failed to build some targets (02:22 (mm:ss)) #### ,以便强制UI线程处理消息。这不是很好的做法,但是 很好地说明了这个问题。