【问题标题】:Running a BackgroundWorker within another BackgroundWorker在另一个 BackgroundWorker 中运行 BackgroundWorker
【发布时间】:2011-03-09 10:39:42
【问题描述】:

我正在一个项目中尝试一项非常密集的数据库任务。这是一个演练:

我们需要搜索我们称为 Locums 的工人数据库,并为特定工作找到一个。当我们决定处理 x 个作业时,此过程开始。因此,在单击按钮时,我们使用ProcessJobBatch() 方法进行处理。但是,此方法仅针对非常有限数量的 Locums 进行处理。因此,填充调度程序控件需要不到 10 秒的时间。现在,一旦为有限数量的 Locums 提供服务,我们需要运行一个后台任务来检查其余的 Locums。大约有 1250 个!

所以,一旦ProcessJobBatch() 完成,BackgroundWorkerBackgroundWorkerMoreLocums 就会消失。现在,这个工人基本上做了一个简单的循环:对于每个工作,遍历整个 1250 名员工。这需要的时间太长了。我需要使用 ATM 无法使用的替代策略来计划这一点,或者我需要为内部 for-each 循环显示辅助进度条。

更多说明:我们每天都会多次导入一批作业(10 到 70 个)。导入批次后,应用程序会指示登录用户“优先查找”那些新创建的作业。用户已经有一个他最喜欢的 locums 列表(1 到 20)。他想首先将工作分配给他最喜欢的人。这是通过ProcessJobBatch() 完成的。但是,有两种情况会阻止那里的流动:

  • 如果某些工作不属于 有什么喜欢的地方吗?
  • 如果整体中有一个 locum 怎么办 DB几乎可以做所有的工作,但 因为他不是最喜欢的?

所以,我最终得到了一个将工作与每个 Locum 匹配的场景。

问题: 第二个 BackgroundWorker 可以在 BackgroundWorker 的 DoWork 中运行吗? 我第二次扫描错了吗?

环境:Windows 7 Pro 64 位、Visual Studio 2010、C#、.NET 4.0 和 Windows 窗体

    private void ButtonPreferenceFind_Click(object sender, EventArgs e) {
        if (LookUpBatches.EditValue != null) {
            JobBatch JobBatchSelected = DbContext.JobBatches.FirstOrDefault(job_batch=> job_batch.OID == LookUpBatches.EditValue.ToString());

            if (JobBatchSelected != null && JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1) > 0) {
                if (XtraMessageBox.Show(String.Format("Are you sure to process {0} job(s)?", JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1)), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
                    ProcessJobBatch(JobBatchSelected);

                    IEnumerable<Job> SpecificJobs = from req_jobs in JobBatchSelected.Jobs
                                                                                    where req_jobs.JobStatusID == 1
                                                                                    select req_jobs;

                    ProgressBarControlPreferenceFinder.EditValue = 0;
                    ProgressBarControlPreferenceFinder.Properties.Minimum = 0;
                    ProgressBarControlPreferenceFinder.Properties.Maximum = SpecificJobs.Count() - 1;

                    BackgroundWorkerMoreLocums.RunWorkerAsync(SpecificJobs);

                } else {
                    LookUpBatches.Focus();
                }

            } else {
                XtraMessageBox.Show("Unable to retrieve the selected batch or the batch has no processable jobs.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                LookUpBatches.Focus();

            }

        } else {
            XtraMessageBox.Show("Select a batch first.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);                   
            LookUpBatches.Focus();

        }
    }

    #region Background Searching
    private void BackgroundWorkerMoreLocums_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
        try {
            e.Result = GetTableData(e.Argument);

        }
        catch (Exception ex) {
            XtraMessageBox.Show("Background Error: " + ex.Message, "Excite Engine 2", MessageBoxButtons.OK, MessageBoxIcon.Error);
            e.Result = ex;
        }
    }

    private void BackgroundWorkerMoreLocums_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
        // only display progress, do not assign it to grid          
        ProgressBarControlPreferenceFinder.Increment(e.ProgressPercentage);
    }

    private void BackgroundWorkerMoreLocums_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
        if (e.Result is DataTable) {
            //dataGridView1.DataSource = e.Result as DataTable;
        }
        else if (e.Result is Exception) {
        }
    }

    private DataTable GetTableData(Object JobList) {
        DataTable ResultDataTable = new DataTable();
        ResultDataTable.Columns.Add();


        IEnumerable<Job> JobBatchSelected = (IEnumerable<Job>)JobList;

        IEnumerable<Locum> LeftOverLocums = from lefties in DbContext.Locums
                                                                                //where SchedulerMatrixStorage.Resources.Items.Select(res => (long)res.Id).ToList().Contains(lefties.OID) == false
                                                                                select lefties;

        int NumOfJobsProcessed = 0;

        List<KeyValuePair<long, TemporaryPreferenceFindLocum>> AlreadyPrefferedLocums = new List<KeyValuePair<long, TemporaryPreferenceFindLocum>>();

        foreach (Job oneJob in JobBatchSelected) {

            foreach (Locum oneLocum in LeftOverLocums) {

                if (DbContext.Availabilities.Any(check => check.LocumID == oneLocum.OID && check.AvailableDate == oneJob.JobDate && check.AvailabilityStatusID == 1)) {
                    //This Locum can do this job

                    //Now check if he/she has been just alloted
                    if (AlreadyPrefferedLocums.Any(search => search.Key == oneLocum.OID && search.Value.JobDate == oneJob.JobDate) == false) {
                        //No? Cool!                     
                        //Add to the list to prevent double allocation
                        AlreadyPrefferedLocums.Add(new KeyValuePair<long, TemporaryPreferenceFindLocum>(oneJob.OID, new TemporaryPreferenceFindLocum(oneJob.JobDate, oneJob.OID, oneLocum.OID, oneLocum.FirstName + " " + oneLocum.LastName)));

                    }
                    else {
                        continue;

                    }

                }
                else {
                    //Not marked as Avaliable on the required job date...
                    continue;

                }

            }

            NumOfJobsProcessed++;
            BackgroundWorkerMoreLocums.ReportProgress((int)(NumOfJobsProcessed * 100F / (JobBatchSelected.Count() - 1)));
        }

        return ResultDataTable;
    }
    #endregion

【问题讨论】:

  • 记住 backgroundWorker 在线程池中执行。你应该能够让一个踢另一个。所以理论上,你应该能够在另一个中运行一个,因为外部的 backgroundworker 正在订阅由内部触发的事件。总体而言,该解决方案听起来像是很多活动部件,但我可能无法正确理解您的伪代码。您能否解释一下为什么 ProcessJobBatch 的容量有限以及您如何确定何时需要处理 locum?

标签: c# .net linq backgroundworker


【解决方案1】:

可以从另一个BackgroundWorkerDoWork 处理程序中启动BackgroundWorker,但您需要注意使用这种方案的后果。当您从主 UI 线程启动后台工作程序时,DoWork 处理程序在线程池线程上执行,而 ProgressChangedRunWorkerCompleted 在主 UI 线程上执行,使您可以安全地与 Windows 窗体交互控制。

当您从主 UI 线程启动工作程序时,可以保证这种情况,因为它会选择该线程上可用的 SynchronizationContext,并由 windows 窗体基础结构初始化。

但是,当您从另一个工作程序的 DoWork 处理程序启动后台工作程序时,您将从缺少同步上下文的线程池线程启动它,从而导致 ProgressChangedRunWorkerCompleted 处理程序也被在线程池线程上执行,而不是在主 UI 线程中执行,这使您在这些处理程序中与 Windows 窗体控件进行交互是不安全的。

【讨论】:

    【解决方案2】:

    让一个后台线程产生新的后台线程是很常见的。如果您在后台线程上扫描列表并在另一个线程上处理每个列表项,我认为这不是问题。

    在这种情况下,没有后台工作人员另一个。只有一个后台工作人员正在启动其他线程。

    你应该考虑的事情 -

    1. 请注意您在完成的事件处理程序中执行的操作,以防您处理该事件。

    2. 考虑为小任务运行这么多线程对性能的影响。您应该考虑使用PLINQ or parallel tasks,以便.Net 可以处理输入的分区和结果的合并,从而为您提供最佳性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多