Running BackgroundWorker in another BackgroundWorker

I am trying to do very intensive work with the database in the project. Here's a walkthrough:

We need to look for our database of workers, we call Locums and find it for a specific job. This procedure begins when we decide to process the x number of jobs. Thus, with the click of a button, we process the ProcessJobBatch() method. However, this method is only processed with a very limited number of Locums. Therefore, it takes less than 10 seconds to populate the scheduler manager. Now that a limited number of Locums is served, we need to run a background job to check out the rest of Locums. There are about 1250 of them!

So, once ProcessJobBatch() , and BackgroundWorker , BackgroundWorkerMoreLocums disabled. Now this worker basically performs a simple cycle: go through all 1250 employees for each job. It takes too long. I need to plan this using an alternative strategy that I cannot use at the ATM, or I need to show a secondary progress indicator for the internal cycle for each.

Details Explanation: We import a batch of tasks (from 10 to 70) on a daily basis daily. As soon as the package is imported, the application indicates to the logged in user "Preference to find" those newly created tasks. The user already has a list of his favorite places (from 1 to 20). First, he wants to distribute the work among his favorites. This is done through ProcessJobBatch() . But there are two scenarios that prevent the flow there and then:

  • What if certain jobs don't fall in any favorite locus?
  • What to do if in general there is a DB slogan that can carry out almost all tasks, but since it is not a favorite?

So, I am finishing the job mapping scenario with each Locum.

Question: Can a second BackgroundWorker work inside a BackgroundWorker DoWork? Am I doing a second scan incorrectly?

Environment: 64-bit version of Windows 7 Pro, Visual Studio 2010, C #, .NET 4.0 and 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 
+4
source share
2 answers

A BackgroundWorker can be launched from a DoWork handler of another BackgroundWorker , but you need to know the implications of using such a scheme. When you start a background worker from the main UI thread, the DoWork handler runs in the thread pool thread, and ProgressChanged and RunWorkerCompleted are executed back in the main user interface thread, which makes it safe to interact with window forms.

This script is guaranteed when the worker starts from the main user interface thread, because it selects the SynchronizationContext available in this thread and which is initialized by the window forms infrastructure.

However, when starting a background worker from another worker's DoWork handler, you will start it from a thread pool thread that does not have a synchronization context that calls ProgressChanged and RunWorkerCompleted also executed in thread pool threads and not in your main user interface thread, which makes it unsafe for interacting with Windows form elements from within these handlers.

+11
source

Typically, a single background thread creates new background threads. I do not think this is a problem if you are viewing a list in a background thread and processing each list item in a different thread.

In such cases, there is no background worker inside another. There is only a background worker running other threads.

Things you should consider -

  • Remember what you do in the event handlers if you handle this event.

  • Consider the performance implications of running a large number of threads for small tasks. You must use PLINQ or parallel tasks so that .Net can handle input splitting and merging results for optimal performance.

+5
source

Source: https://habr.com/ru/post/1342976/


All Articles