1. Introduction
BackgroundWorker component is used to perform long running tasks in
the background while the application running in the foreground will still look
for the user events and responds to them. Usually when application is busy
(i.e.) performing a long running task, users of the application cannot
interact with the UI elements and the only existing thread is engaged with that
long running task.
Here, we created a sample that counts vowels of the
textual content given in the text box. To make that as a long running task, we
asked the execution to sleep for 50 milliseconds after processing a single
character.
2. About the Sample
The sample created
using the Background worker thread is shown below:
![]() |
| Pic1: BackgroundWorker Example App |
When you click
the Start Count the sample will start counting the vowels and
displays that in the bottom count meters (i.e.) in the text boxes
for A, E, I, O, U. In the meantime a progress bar will show the progress of the
operation. The counting process is made intentionally slow, by making use
thread.sleep. The cancel button is provided to stop the counting
process in the middle.
When both the
check boxes are in un-checked state and, when the sample is counting the
vowels, you cannot interact with the form. The “Use Doevents“
checkbox will apply doevents and shows you that you can interact with the form.
But, when there is an interaction, counting process stops and after interaction
completed the counting resumes. Use heavy interaction like moving the form or
resizing the form.
The “Use
Background Worker” option will overcome the drawback of do only one
stuff at once (i.e.) now with the back ground worker component you can see the
progress update and vowels count meter increasing continuously even when you
resize the form.
Source Code Search Tag: //BWSamp
3. Vowel counter functions
The below
function checks and increments the vowel count for A.
//BWSamp 01: Functions that count and Updates vowel counters.
private void CheckVowelA(char c)
{
long Vowel_count;
if (c == 'A' || c == 'a')
{
if
(chkUseBackground.CheckState != CheckState.Checked)
{
long.TryParse(txtA.Text, out Vowel_count);
Vowel_count++;
txtA.Text =
Vowel_count.ToString();
}
else
count_a =
count_a + 1;
}
}
The CheckVowelA
function checks the passed in value to make sure it is a vowel A. Also note
that when the “Form” is in background thread mode we just increment a variable count_a.
When we are not using the Back Ground thread component, the vowel meter for A
is incremented (i.e.) the text box for A is set with the incremented value. A
similar function is written for all other four vowels.
At present you
may have a question, why we have a separate variable usage count_a
for background worker component. I will come to that point later in this
article.
4. Add the BackgroundWorker Component
When we know
that the form should do a long running task in the background and in the mean
time user can still interact with the form to do some useful work, the
BackgroundWorker component is a best choice to deal with such a situation. The
video given below shows adding the Background Worker component:
Video Steps
1) Adds the
BackgroundWorker component to the Form
2) Sets the name
for the component
3) Then provided
handler for all three events supported by it.
BackgroundWorker
is a component and that is why it does not occupy any space in the form. The
components unlike “Controls” don’t have visual appearance and hence the
components go and sit at the bottom of the form designer when it is dropped in
the form. In the video we handled three events and we will explore that later.
Note
that after adding the component we should set two important properties that
will enable the stopping the worker thread when the task is in progress,
sending the progress of the running task. These two properties are shown below:
![]() |
| Pic2: WorkReportsProgress |
5. Class Scope variables
We are using cancel_work
variable to stop the vowel counting operation. This variable is used in
counting operation when backgroundworker component is not used. The variables
count_a, count_e, count_I, count_o and count_u are used to keep track the vowel
counts in the background work mode vowel counting action. Below are
declarations:
//BWSamp 04: Cancel Action
private bool cancel_work = false;
long count_a, count_e,
count_i, count_o, count_u;
6. Start Count Handler
In the start
count button handler we are initializing the vowel counting variables and then
setting the cancel_work to false. Based on the chkUseBackground checkbox state we start
the vowel counting operation either in single threaded mode or two threaded
mode. The call to DoWithBKGrd function will not use any
background thread while performing the count operation and the function
operates in single thread mode. The call RunWorkerAsync() that we
make on the backgroundworker component (Named as BckThread) will call the DoWork
handler of the same component. At present we don’t have that handler and
provide that in a short moment. This dowork runs on a separate thread and all
the rest of code runs in a different thread. Below is the code for Start button
click
//BWSamp 02: Simulate Long Running Task
private void btnStart_Click(object sender, EventArgs e)
{
//2.1: Set Cancel Work as false when the job starts
cancel_work = false;
count_a = 0;
count_e = 0;
count_i = 0;
count_o = 0;
count_u = 0;
//2.2: To use back ground worker, call RunWorkerAsync
if
(chkUseBackground.CheckState != CheckState.Checked)
{
DoWithoutBKGrd();
}
else
{
BckThread.RunWorkerAsync();
}
}
7. Long Running Task – Without Background Worker
In this section
I am going to explain the source code implementation for the DoWithoutBKGrd Function.
1) From the textbox we are getting the text content into a string
variable called strcontent and length of the strcontent is stored in the
len variable. Then we divide the total lengths into 100 parts, storing
the length single part in a variable Prog_Inc. This variable is
useful to show the status of the operation in a progress bar in an accurate
manner (i.e.) if progress is in the middle 50% of job completed.
//3.1: Get text length
string strcontent =
txtContent.Text;
long len = strcontent.Length;
int Prog_Inc = (int)(len / 100);
2) As we are going to count vowels in the set of paragraphs displayed
in the big text box, it will be useful to convert all the string content to a
char array. The below piece of code is doing just that.
//3.2: Copy the string content to char array
char[] chars =
strcontent.ToCharArray();
3) Once we have text box content in the char array format, we can
examine each of those letters to see whether it is a vowel or not. We are using
a loop to iterate through the entire letter extracted from the textbox into an
array. For each letter we are making call to CheckVowelA-U. And those functions
will do the required stuff (Refer Section 3). Note that in each of the
iteration we check the cancel_work variable, and when it is set we reset the
counter and return back. Below is the piece of code:
//3.3: Iterate through each char and increment the Vowels count
for (int itr = 0; itr < len;
itr++)
{
if (cancel_work == false)
{
char c = chars[itr];
lblEvtComplete.Text = "Count
In-Progress";
//3.3.1: Count for A
CheckVowelA(c);
//3.3.2: Count for E
CheckVowelE(c);
//3.3.3: Count for I
CheckVowelI(c);
//3.3.4: Count for O
CheckVowelO(c);
//3.3.5: Count for U
CheckVowelU(c);
.
.
.
.
else
{
txtA.Text = "0";
txtE.Text = "0";
txtI.Text = "0";
txtO.Text = "0";
txtU.Text = "0";
lblEvtComplete.Text = "Counts Cancelled";
return;
}
4) The Prog_Inc
variable holds a value that says when should we do a progress increment of one
unit in terms of number of letters processed for vowel counting. If we
divide the current progress (the loop counter) by the Prog_Inc, we will get the
progress percentage. The variable prog_value holds the progress bar percentage
and sets that to the progress bar. The 10 Milli seconds sleeps is to simulate
this counting operation as long running operation. In this long running
iteration loop we called the DoEvents, when the Use DoEvents
checkbox is checked.
//3.4: Update Progress bar
int prog_value = (itr /
Prog_Inc);
if (prog_value > 100)
prog_value = 100;
ProgressB.Value = prog_value;
Thread.Sleep(10);
if (chkUseDoevents.CheckState
== CheckState.Checked )
Application.DoEvents();
The
doevent() function call does not spawn a different thread and it runs in the
same thread. But do event will stops current counting operation and performs
all the pending UI tasks (Say the user resized the form while vowel counting in
progress), then resumes back to the counting operation.
5) When
we check the check box stating we want to use the back ground thread for
counting the vowels, the use do events option gets disabled. The code for this
is listed below:
//BWSamp 06: When using BkGrd component, uncheck and disable
doevents
private void chkUseBackground_Click(object sender, EventArgs e)
{
if
(chkUseBackground.CheckState == CheckState.Checked)
{
chkUseDoevents.Enabled
= false;
chkUseDoevents.CheckState = CheckState.Unchecked;
}
else
chkUseDoevents.Enabled = true;
}
8. Canceling the Long running Operation
As Already told,
the simulated long running operation can be cancelled in between by clicking
the cancel button. We set the cancel_work variable to true and
this variable is used by the counting operation running on the same thread.
When the counting operation is performed on the background thread, we are
making a call to the BackgroundWorker component’s member function CancelAsync.
This function call will set the Background Worker component’s CancellationPending property to true and this property is checked in the long
running task.
//BWSamp 05: Set the Cancel Flag to cancel the current active job
private void btnCancel_Click(object sender, EventArgs e)
{
cancel_work = true;
if
(chkUseBackground.CheckState == CheckState.Checked)
BckThread.CancelAsync();
}
9. DoWork event handler of BackgroundWorker
There are
three events important event that you should handle (Two are optional actually.
Loon at the Pic2) and these events are:
1) DoWork
2)
ProgressChanged
3)
BckThread_RunWorkerCompleted
The dowork
event will actually fired by the control when we call background worker thread
component’s method RunWorkerAsync. You can perform the long
running task in the event handler for the DoWork event. This event handler runs
on the separate thread and while other two runs on the thread that starts the
background worker thread. In out case the form is the main thread that spawns
the UI thread by making a call to RunWorkerAsync in the Start
Count button click.
![]() |
| Pic 3: Interaction Between Main Thread and Background Thread |
In the above
picture, the main thread (The form) starts the background thread (sub thread),
which is nothing but the doWork event handler of the backgroundworker
component. Inside this modifying the UI components of the main thread
is not allowed. Say for example we want to show the progress of the dowork in
the progress bar and like to update vowel counts real time when counting is
performed. But we cannot access those two user interface controls of the form
from the Do Work handler as it is running in the separate thread.
But the do work
handler is helps the Main thread through the ProgressChanged and RunWorkerCompleted
events. Note that these two events are raised by the worker thread and handled
by the main thread. These handlers will run on the main thread so we can do
whatever UI updates we like to do. The worker components function call ReportProgress will raise the event ProgressChanged.
Once the dowork handler returns, the event RunWorkerCompleted
will be raised.
Let dig into the
code given in the do work handler of the background component.
1) After getting
the length of the entire string content, we are dividing that string length
into 100, so that we can inform the progress from 0 to 100. Below is the piece
of code:
//7.1: Get text length
string strcontent = txtContent.Text;
long len = strcontent.Length;
int Prog_Inc = (int)(len /
100);
2) Next we
convert entire string into a character array, then process the letters one by
one and make sure it is vowels. When it is vowel we increment the count meter
by one inside vowel checking functions. Below is the code (Almost equal to what
we did in section 7) for it:
//7.2: Copy the string content to char array
char[] chars = strcontent.ToCharArray();
//7.3: Iterate through each char and increment the Vowels count
for (int itr = 0; itr < len;
itr++)
{
char c =
chars[itr];
.
.
.
//7.3.2: Count for A
CheckVowelA(c);
//7.3.3: Count for E
CheckVowelE(c);
//7.3.4: Count for I
CheckVowelI(c);
//7.3.5: Count for O
CheckVowelO(c);
//7.3.6: Count for U
CheckVowelU(c);
3) In the
iterations for each letters, we check for the CancellationPending
property to make sure main thread wants to terminate the current work that is
in progress. Note that the owner of this do work thread can terminate this
thread by anytime by making a call to the method CancelAsync of
the backgroundworker component. The above said function call will
set the CancellationPending property of the Backgroundworker to true. When we
see there is a cancellation pending we simply return from the background
thread. Below is the piece of code
//7.3.2: Check the Cancel State
if (BckThread.CancellationPending == true)
{
e.Cancel = true;
return;
}
4) After
processing each letters, we report the progress by making the call to the
method ReportProgress. This method will fire the event ProgressChanged
and you know that we already have the handler for that in the form (Not yet
implemented). You can refer video1 towards its end. Prog_inc is 1/100th
part of length of the string and we calculate how much we progressed in the prog_value
using the Prog_Inc. We send this calculated current progress
value to the ReportProgress function. Even though we cannot modify the UI
elements from the Background Job thread, we can do the same from the event
handler for ProgressChanged as this handler runs in the context of main thread
(Refer Pic3).
//7.4: Update Progress bar
int prog_value = (itr / Prog_Inc);
if (prog_value > 100)
prog_value = 100;
BckThread.ReportProgress(prog_value);
Thread.Sleep(10);
10. Updating the UI in the Progress Changed Event Handler
The code in the
Progress Changed event handler is straightforward. We get progress percentage
through the ProgressChangedEventArgs that we receive as argument
to the event handler. In the progress changed function we set the progress bar
value and in the mean time we also update the vowel count meters based on the
values in the class level variables. Remember? We pass Prog_value to the
function BckThread.ReportProgress(prog_value),
and that value is collected here as event handler argument.
//BWSamp 08: Handle Progress Changed of Bck Worker Thread
private void
BckThread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressB.Value =
e.ProgressPercentage;
txtA.Text =
count_a.ToString();
txtE.Text =
count_e.ToString();
txtI.Text = count_i.ToString();
txtO.Text =
count_o.ToString();
txtU.Text =
count_u.ToString();
}
11. Updating the UI through Runworker completed Handler
The code below
shows setting a label with the notification of back ground task completed in
the handler for the RunWorkerCompleted. There is nothing special
that needs to be discussed here.
//BWSamp09: Handle Bck Task completed
private void
BckThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
lblEvtComplete.Text = "Cancelled";
else
lblEvtComplete.Text = "Completed";
}
12. Running the Sample Application
1) First the
sample is run in a normal mode. That is we run the sample without background
worker component and we don’t used do DoEvent also. In the video you can see
the counting operation is started and after that a form resize is tried. This
makes the form to move to Not Responding stage, and once the counting operation
is completed you get the updated vowel count meter.
Video Steps
1) The sample
exe is started from the explorer
2) After showing
the form, start count button is clicked
3) Clicking on
the Window Restore option makes the sample in Not Responding Stage (i.e. it is
busy with counting vowels)
4) Clicking the
cancel button Fades-out the form (Kind of dead) informing the user that sample
is not ready and still busy.
5) Waiting for
the counting to be completed. Once done the form will become alive
2) The
above video shows the need for Back ground work. In the below video the usage
of DoEvent is shown. You can notice, even though the Progress Bar and Count
meter update is smooth, when we move the form for three or four seconds, the
count operation is suspended and resumes back when the move or resize operation
of form is completed. This is shown as visual indication as when the for is
moved the progress bar and count meter update is stopped. Check this in the
below video.
Video Steps
1) Sample application is started
2) Use DoEvents
check box is checked
3) Shows the Progress Bar
Increment and Count Meter update
4) Resizes the form and shows the
continuous break in the Progress and Count meter Update.
Somewhat better compared the
previous as this time we use the DoEvent on long running counting operation.
3) In this last video we used the
BackgroundWorker component to show how smooth and real-time the operation of
counting as well as updating the Foreground UI form. When you resize or move
the form you can see the un-interrupted counting as well as progress update to
the Form UI. This is the power of BackGroundWorker component.
What we shown here is a simple sample that perform vowel count intentionally
slow. In real world, the background will do some serious task not blocking the
foreground UI, and there by user can still interact with the form and do some
useful application interaction while the work started by him/her is still going
on the back ground.
Video Steps
1)
Sample is started
2)
Use Background Worker check box is selected (DoEvents disabled)
3)
Start Count Button is clicked
4)
Form is Resized, text box content is changed while back ground job is going on
Source Code: Download



Excellent Post... Thanks much for sharing :)
ReplyDeleteI used to be recommended this website by means of my cousin.
ReplyDeleteI am no longer sure whether this submit is written by way of him
as nobody else recognize such particular approximately my trouble.
You're amazing! Thank you!
My weblog : Personal Trainer Mission Valley