.NET - Using the BackGroundWorker to handle multiple tasks

I'm using VB.NET, and I'm currently using two Timer objects - one to monitor the external operation, and a second to perform the db writes.

I've been advised to look into using the BackgroundWorker object instead, and wanted to better understand exactly what this does, and how I'd benefit from using it. I'm hoping for some insight on how the BGW object works, and why it would be better than a Timer object.

Essentially my program interacts with an automated "picking" system. My program writes rows to an integration table. The pick system reads data from those integration tables, and then performs tasks. The pick system reports success by removingthen removes rows from those tables, or writes values into an Error table, and my program responds to those actions. The process boils down to this:

1. My program adds rows to a Detail and Header table (the "integration" tables)
2. "Pick system" reads those rows and performs tasks.
3. "Pick system" removes the rows from those tables upon a successful "pick", or writes records to an Error table upon failure
4. Timer1 in my program queries those tables periodically, and adds values to a Queue object for successful picks.
5. Timer2 reads the Queue objects, and writes values to another database based on the Queue object.

Step 4 is the first Timer. As mentioned above, it checks the integration tables. If there is a successful pick, I add items to a queue.

Step 5 is the second Timer. It reads the next Queue item and attempts to write records to a database. This operation can take some time, depending on the complexity of the data.

My question is - what's the benefit in using the BackGroundWorker instead of the Timer? The Timer object seems ... klunky ... whereas the BGW object seems better suited for what I'm doing - but I don't really understand the BGW and how it works.
LVL 86
Scott McDaniel (Microsoft Access MVP - EE MVE )Infotrakker SoftwareAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Kyle AbrahamsSenior .Net DeveloperCommented:
from MSDN:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/fed51ca1-8ade-4826-9b3d-24c1a913dae5/backgroundworker-vs-timer

BackgroundWorkers and Timers are really intended for different purposes.

A BackgroundWorker should be used if you have actual "work" (processing) that should be done right now , but on a background thread, so you don't avoid locking up your GUI.  This works via a background thread in the thread pool, and doesn't use timers at all.

A Timer, on the other hand, is meant to notify you at a point in the future , so you can do work that's appropriate at that point.

The two classes really are meant for different purposes altogether.


Use:
Timer - if the target method needs to be invoked based on a static timed duration.
BackgroundWorker - if the target method needs to be invoked based on either the object's response to another action or an event taking place.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Scott McDaniel (Microsoft Access MVP - EE MVE )Infotrakker SoftwareAuthor Commented:
Thanks for that information - I realized after I wrote it that a Timer and a BackgroundWorker are really different, and that they can be used in conjunction with each other.

My thoughts are to use the Timer to periodically check for the necessary markers to begin my process, and to then use the BackgroundWorker to actually perform the database write. That would help prevent my program from freezing during the (possibly) long-running database operations.
it_saigeDeveloperCommented:
Also instead of Timers, you could use reset events.  This would give you more granularity in your control so that you could immediately signal that a thread continue processing.

A very basic example (I've used something similar with other events to control the signaling of the processing as well as within services so that I could stop my threads from processing)
Imports System.ComponentModel
Imports System.Threading

Module Module1
	Private PickQueue As New Queue(Of Pick)
	Private WithEvents Picker As New BackgroundWorker()
	Private WithEvents ProcessPicks As New BackgroundWorker()
	Private PickReset As New ManualResetEvent(False)
	Private IsPicking As Boolean
	Private IsProcessPicking As Boolean
	Private HasPicked As Boolean

	Sub Main()
		Picker.WorkerReportsProgress = True
		Picker.WorkerSupportsCancellation = True
		ProcessPicks.WorkerReportsProgress = True
		ProcessPicks.WorkerSupportsCancellation = True
		IsPicking = True
		IsProcessPicking = True
		Picker.RunWorkerAsync()
		ProcessPicks.RunWorkerAsync()
		While Not HasPicked
			PickReset.Set()
		End While
		Picker.CancelAsync()
		PickReset.Set()
		Console.ReadLine()
	End Sub

	Sub OnPickerWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles Picker.DoWork
		If TypeOf sender Is BackgroundWorker Then
			Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
			Dim count As Integer = 0
			While (IsPicking)
				If worker.CancellationPending Then
					e.Cancel = True
					ProcessPicks.CancelAsync()
					Exit Sub
				End If

				If Not HasPicked Then
					For i As Integer = 1 To 10
						Dim pick = New Pick() With {.ID = i + count, .Name = String.Format("Pick{0}", i + count)}
						PickQueue.Enqueue(pick)
						worker.ReportProgress(0, New PickState() With {.Pick = pick})
					Next
					count = count + 10
				End If

				If count = 100 Then
					HasPicked = True
				End If
				PickReset.WaitOne(5000)
			End While
		End If
	End Sub

	Sub OnProcessPicksWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles ProcessPicks.DoWork
		If TypeOf sender Is BackgroundWorker Then
			Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
			While (IsProcessPicking)
				If worker.CancellationPending Then
					While PickQueue.Count > 1
						Dim finish As Pick = PickQueue.Dequeue()
						If finish IsNot Nothing Then
							If finish.ID Mod 5 <> 0 Then
								worker.ReportProgress(0, New PickState() With {.Pick = finish, .IsAdded = True})
							Else
								worker.ReportProgress(0, New PickState() With {.Pick = finish, .IsAdded = False, .Exception = New Exception(String.Format("Pick {0} could not be added.", finish.ID))})
							End If
						End If
					End While
					e.Cancel = True
					Exit Sub
				End If
				Dim current As Pick = PickQueue.Dequeue()
				If current IsNot Nothing Then
					If current.ID Mod 5 <> 0 Then
						worker.ReportProgress(0, New PickState() With {.Pick = current, .IsAdded = True})
					Else
						worker.ReportProgress(0, New PickState() With {.Pick = current, .IsAdded = False, .Exception = New Exception(String.Format("Pick {0} could not be added.", current.ID))})
					End If
				End If
			End While
		End If
	End Sub

	Sub OnPickerProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles Picker.ProgressChanged
		Dim state As PickState = DirectCast(e.UserState, PickState)
		Console.WriteLine("Queued {0} with an ID of {1}", state.Pick.Name, state.Pick.ID)
	End Sub

	Sub OnProcessPicksProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles ProcessPicks.ProgressChanged
		Dim state As PickState = DirectCast(e.UserState, PickState)
		If state.IsAdded Then
			Console.WriteLine("Processing {0} with an ID of {1}", state.Pick.Name, state.Pick.ID)
		Else
			Console.WriteLine(state.Exception.Message)
		End If
	End Sub

	Sub OnPickerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles Picker.RunWorkerCompleted
		If e.Cancelled = True Then
			Console.WriteLine("Picker Canceled!")
		ElseIf e.Error IsNot Nothing Then
			Console.WriteLine("Picker Error: {0}", e.Error.Message)
		Else
			Console.WriteLine("Picker Completed")
		End If
	End Sub

	Sub OnProcessPicksCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles ProcessPicks.RunWorkerCompleted
		If e.Cancelled = True Then
			Console.WriteLine("Process Queue Canceled!")
		ElseIf e.Error IsNot Nothing Then
			Console.WriteLine("Process Queue Error: {0}", e.Error.Message)
		Else
			Console.WriteLine("Process Queue Completed")
		End If
	End Sub
End Module

Class Pick
	Public Property ID() As Integer
	Public Property Name() As String
End Class

Class PickState
	Public Property Pick() As Pick
	Public Property IsAdded() As Boolean
	Public Property Exception() As Exception
End Class

Open in new window


-saige-
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

SStoryCommented:
Scott,

These are already good enough explanations.  BackgroundWorker is sort of managed threading in a way.   Provide an object with work to do and get back the results when it is done.
Scott McDaniel (Microsoft Access MVP - EE MVE )Infotrakker SoftwareAuthor Commented:
Also instead of Timers, you could use reset events
I like this idea. I'm reviewing your code and will post a new question if needed.
Scott McDaniel (Microsoft Access MVP - EE MVE )Infotrakker SoftwareAuthor Commented:
Thanks for the information!
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.