Threading in C#
Hi Experts,
I have a question which I want to seek assistance for. Hope you guys can help me am much as possible.
I have an event where I have to read an Excel File and plot a graph after I click on a button. However, after clicking on the button and the plotting begins, the problem arises.
The program form cannot be interrupted midway of the plotting as the program form appeared to be 'stuck'. It is not until after the plotting is completed when the program form can be clicked on again.
I understand threading can be used to multi task in C#, but I do not know how to do threading as my programming skills are elementary.
Is threading applicable to this issue? Or are there other ways of making sure the program form can be used while plotting is underway?
Hope to hear from someone soon.
Thanks
Hi!
You could use a simple component called BackgroundWorker to create multithreading application. You can drag and drop the BackgroundWorker to your form in Visual C#, and then create event handlers to it. In those handlers, you can do the time consuming operations and then inform your program when the task is ready. Here is the BackgroundWorker documentation: http://msdn2.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx.
If you find that BackgroundWorker is not enough for your needs, you must use Thread class to start a new thread. But beware, you can run in (commom) errors when dealing with threads in GUI applications, namely that you cannot modify your GUI in foreing thread. But if you use BackgroundWorker, I remember that you need not to worry about those issues.
here's simple steps how the get BackgroundWorker working:
1. Create Windows application
2. Drag&Drop BackgroundWorker to your form (inside components section)
3. Select backgroundWorker1, and go to events tab in the Properties pane
4. Double click DoWork and RunWorkerCompleted
5. Start your work in DoWork event handler "backgroundWorker1_DoWork"
6. You can inform user about completion in RunWorkerCompleted event handler
7. Add button to your form, and write a following code to it:
if( !backgroundWorker1.IsBusy )
{
backgroundWorker1.RunWorkerAsync();
}
Thats it!
Hi rauhanlinnake,
Thanks for your advice. I shall try it out myself first. Any questions I will post for you advice again.
Thanks
Hi rauhanlinnake,
I have managed to put my time consuming operations into background workers. But I have a problem stopping the operation.
After starting backgroundWorker1, is it true that backgroundWorker1.CancelAsync stops the process? If it is so, how come when I use CancelAsync it doesn't stop the process?
Thanks
Ah, yes. I wondered that one myself once.
Here is the thing, the cancellation does not happen automatically. This would mean that the thread is killed by force, and that could lead to errors.
Have a look at this link: http://www.devsource.com/article2/0,1759,2025927,00.asp
There is some information about cancelling the operation.
For example if your worker is something like this:
for(int i=0; i<10; i++)
{
System.Threading.Thread.Sleep(500);
if(backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
That simulates work and cancels if necessary. But if you use method which is not your own make and not modifiable, cancelling can be a little more troublesome. If you are just starting an operation by calling another method, remember passing the event args object to it. And WorkerSupportsCancelling must be set to true.
backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
MyTimeConsumingOperation( e );
}
I know this can look little bit hard to cancel the operation, but that is a safe way to do it. If you are reading the file in a loop, put the cancellation if at the beginning of that loop.
void MyTimeConsumingOperation( DoWorkEventArgs e )
{
....
while( file.Peek() )
{
if(backgroundWorker1.CancellationPending)
{
e.Cancel = true;
//here close the file handle and other reserved resources
file.Close();
return;
}
//Here read next line of the file etc
}
....
}
And one more thing, in the RunWorkerCompleted event handler, you can get the information if operation was cancelled, or something went wrong, by inspecting the RunWorkerCompletedEventArgs parameter. It can also be used to return data from worker, via Result property.
Hope this helps!
Hi rauhanlinnake,
Thanks for your wonderful advice. I have managed to stop the backgroundworker and it really helped me a lot.
You sad by inspecting RunWorkerCompletedEventArgs parameter, I can get information on errors and data return. How should I do it? What kind od data does it return?
Next thing I would like to ask: Can backgroundWorker be paused and resumed by button clicks?
Thanks a lot
Hi!
The results:
Here is the documentation for RunWorkerCompletedEventArgs (click the members link at the bottom of the page): http://msdn2.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.aspx
The most important properties for RunWorkerCompletedEventArgs object are:
object Result
bool Cancelled
Exception Error
You can add a "return" value to the Result property in your worker. You can return any data you like, because Result is an object typed property
Code Block
void MyTimeConsumingOperation( DoWorkEventArgs e )
{
...
//do your work here, and now you can add a "return" value
...
e.Result = myCalculatedResult; // you can "return" any data you like
}
Cancelled property you should be already familiar with
That contains the same value you set in your worker - true if you cancelled, false if the function succeeded. Error property contains the exception possibly happened during the operation, and you do not have to assign the value yourself (you of course can if you need to, by throwing an exception). The background worker assigns a value to it if something goes wrong in your worker method (exception occures). You should inspect the values something like this:
Code Block
//completed handler
if( e.Cancelled )
{
//Do something needed here
}
else
{
if( e.Error == null )
{
//Everything was fine, you can do something with e.Result
}
else
{
//Inform user about the error
}
}
Pausing:
You can achieve pausing by using a UserState property of the DoWorkEventArgs and RunWorkerCompletedEventArgs. You have to do some modifications, and save the worker's state if you pause. The best approach is to create an object for the worker, so it can remember its state easily manageable way.
Here is one way you can to do to achieve pausing (I have no change to test this now, I have no Visual C# installed, and i have never done this before
)
1) You can create an enum so you can keep track about the progress:
Code Block
public enum MyWorkerState
{
NotStarted,
Working,
PausePending, //use this to pause
Paused, //this indicates that worker is paused and saved its state
Resuming, //use this to resume work
Finished,
Cancelled //this can be left out, added just to be strict ;)
}
2) In your worker method, you have to create a switch structure to handle the state:
Code Block
void MyTimeConsumingOperation( DoWorkEventArgs e )
{
...
while( file.Peek() )
{
...
//this switch needs to be called regulary, so the state changes can be taken in account
switch( (MyWorkerState)backgroundWorker1.UserState )
{
case MyWorkerState.NotStarted:
backgroundWorker1.UserState = MyWorkerState.Working;
//Just continue working
case MyWorkerState.Working:
//here continue the actual work, when finished change status to Finished
//you existing file parsing logic goes here
break;
case MyWorkerState.PausePending:
//here save needed data, so the work can be resumed, and close file handles etc
//when done change status
backgroundWorker1.UserState = MyWorkerState.Paused;
break;
case MyWorkerState.Paused:
//nothing need to be done here (you even shouldn't ever come here)
break;
case MyWorkerState.Resuming:
//here reload needed data, and open needed files
//also change status
backgroundWorker1.UserState = MyWorkerState.Working;
break;
case MyWorkerState.Finished:
//work already completed, no need to do anything (all actions done in the Working case)
// you can make sure here that file etc are closed
break;
case MyWorkerState.Cancelled:
//nothing need to be done here, you can ofcourse cancel here if you like
e.Cancelled = true;
break;
default:
throw new InvalidOperationException("Unknown MyWorkerState");
break;
}
...
}
}
here is example how to use following worker:
Code Block
//start button
backgroundWorker1.RunWorkerAsync(myParameter); //did you know you can add parameters to worker? You can get is in the worker using e.Argument property of the DoWorkEventargs
...
...
//pause button
if( (MyWorkerState)backgroundWorker1.UserState == MyWorkerState.Working)
backgroundWorker1.UserState = MyWorkerState.PausePending;
...
...
//resume button
if( (MyWorkerState)backgroundWorker1.UserState == MyWorkerState.Paused)
backgroundWorker1.UserState = MyWorkerState.Resuming;
In your work completed event handler, you have also to inspect the UserState, because every time you change the UserState (in this case) to a state PausePending, (Paused), (Finished) or Cancelled, the worker QUITS and does to completed event handler.
This might seem a little confusing at the start, but when you implement it, it should be much clearer
If someone know a better way to pause background worker, let us know ;D
Br,
Antti
I found an error in my previous post, but i better not start editing it right now

There error is that you most likely must create a check in the beginning of the worker if the work can be continued, because the loop which needs the file to be open.
Something like this:
MyWorkerState state = (MyWorkerState)backgroundWorker1.UserState;
if( state == MyWorkerState.NotStarted ||
state == MyWorkerState.Paused ||
state == MyWorkerState.Resuming )
{
//it is safe to proceed to the loop, just reopen the file if needed
}
else
{
throw InvalidOperationException("Invalid MyWorkerState");
}
But remember, pausing the thread this way is much better than forcing threads to be stopped! This is because you can pause for a long time without having unnecessary memory allocated, and files reserved. You can even continue the work if you close the program or your computer, if you save and load the workers state from a file! How cool is that
PS. If i have time later, I can try to create an example application and test the pausing and create more complete example about the pausing. I'm quite interested about this right now, I have a project I could use this my self
Ok, I will try out your codes too. If there is a more feasible please let me know too by posting it up.
Thanks
Hi rauhanlinnake
The pausing part is very confusing that I don't even know how to implement it into my program. I'm stuck at the very first step, which is UserState property. I do not know where to declare that. Sounds miserable right?
Can anyone aid me? I've been stuck for a day -.-
Thanks