Запуск BackgroundWorker в другом BackgroundWorker - PullRequest
5 голосов
/ 09 марта 2011

Я пытаюсь выполнить очень интенсивную работу с БД в проекте. Вот краткий обзор:

Нам нужно найти в нашей базе данных работников, которую мы назвали Locums, и найти одну для конкретной работы. Эта процедура начинается, когда мы решаем обработать х количество заданий. Итак, одним нажатием кнопки мы обрабатываем, используя метод ProcessJobBatch(). Однако этот метод обрабатывает только очень ограниченное количество Locums. Таким образом, заполнение элемента управления планировщика занимает менее 10 секунд. Теперь, когда обслуживается ограниченное количество Locums, нам нужно запустить фоновое задание, чтобы проверить остальные Locums. Их около 1250!

Итак, как только ProcessJobBatch() закончится, BackgroundWorker, BackgroundWorkerMoreLocums исчезнет. Теперь этот работник в основном выполняет простой цикл: для каждой работы нужно пройти всего 1250 сотрудников. Это занимает слишком много времени. Мне нужно спланировать это, используя альтернативную стратегию, которую я не могу использовать в банкоматах, или мне нужно показать дополнительный индикатор выполнения для внутреннего цикла for-each.

Подробнее Объяснение: Мы импортируем партию рабочих мест (от 10 до 70) несколько раз в день. Как только пакет импортирован, приложение инструктирует вошедшего в систему пользователя «Поиск предпочтений» для вновь созданных заданий. У пользователя уже есть список его любимых локаций (от 1 до 20). Он хочет сначала распределить работу среди своих любимцев. Это делается через ProcessJobBatch(). Но есть два сценария, которые мешают потоку тут же:

  • Что если определенные рабочие места не упали Любой любимый язык?
  • Что если в целом есть место обитания? БД, которая может делать почти все работы, но так как он не любимый?

Итак, я получаю сценарий соответствия задания каждому Locum.

Вопрос: Может ли второй BackgroundWorker работать в DoWork BackgroundWorker? Я делаю второе сканирование неправильно?

Среда: 64-разрядная версия Windows 7 Pro, Visual Studio 2010, C #, .NET 4.0 и Windows Forms

    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

Ответы [ 2 ]

13 голосов
/ 09 марта 2011

A BackgroundWorker можно запустить из обработчика DoWork другого BackgroundWorker, но вам необходимо знать о последствиях использования такой схемы.Когда вы запускаете фонового работника из основного потока пользовательского интерфейса, обработчик DoWork выполняется в потоке пула потоков, а ProgressChanged и RunWorkerCompleted выполняются обратно в основном потоке пользовательского интерфейса, что делает его безопасным для взаимодействия с формами Windows.controls.

Этот сценарий гарантирован, когда вы запускаете работника из основного потока пользовательского интерфейса, поскольку он выбирает SynchronizationContext, доступный в этом потоке и инициализируемый окнами, образует инфраструктуру.

Однако, когда вы запускаете фонового работника из обработчика DoWork другого работника, вы запускаете его из потока пула потоков, в котором отсутствует контекст синхронизации, вызывая обработчики ProgressChanged и RunWorkerCompletedтакже выполняется в потоках пула потоков, а не в основном потоке пользовательского интерфейса, что делает небезопасным взаимодействие с элементами управления форм Windows из этих обработчиков.

5 голосов
/ 09 марта 2011

Довольно часто один фоновый поток порождает новые фоновые потоки.Я не думаю, что это проблема, если вы сканируете список в фоновом потоке и обрабатываете каждый элемент списка в другом потоке.

В таких случаях нет фонового рабочего внутри другого.Есть только фоновый работник, запускающий другие потоки.

Вещи, которые вы должны учитывать -

  1. Знайте, что вы делаете в обработчиках завершенных событий, если вы обрабатываете это событие.

  2. Учитывайте влияние на производительность запуска множества потоков для небольших задач.Вам следует рассмотреть возможность использования PLINQ или параллельных задач , чтобы .Net могла обрабатывать входные данные и объединять результаты, обеспечивая оптимальную производительность.

...