C# 2008
I am using the code below to login to a softphone. However, the login progess is a long process as there are many things that have to be initialized and checks to be made, I have only put a few on here, as it would make the code to long to post.
In the code below I am checking if the CancellationPending if the CancelAsync has been called in my cancel button click event, before doing each check. Is this correct? Also if the check fails I also call the CancelAsync and set the e.Cancel to true.
I would like to know if my method I have used here is the best method to use.
Many thanks for any advice,
private void bgwProcessLogin_DoWork(object sender, DoWorkEventArgs e)
{
/*
* Perform at test to see if the background worker has been
* cancelled by the user before attemping to continue to login.
*
* Cancel background worker on any failed attemp to login
*/
// Start with cancel being false as to reset this if cancel has been set to true
// in the cancel button.
e.Cancel = false;
NetworkingTest connection_test = new NetworkingTest();
if (!this.bgwProcessLogin.CancellationPending)
{
// Check local LAN or Wireless connection
if (!connection_test.IsNetworkConnected())
{
// Update label
if (this.lblRegistering.InvokeRequired)
{
this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "No network connection");
}
else
{
this.lblRegistering.Text = "No network connection";
}
// Failed attemp
this.bgwProcessLogin.CancelAsync();
e.Cancel = true;
return;
}
// Report current progress
this.bgwProcessLogin.ReportProgress(0, "Network connected");
}
else
{
// User cancelled
e.Cancel = true;
return;
}
// Test if access to Server is available
if (!this.bgwProcessLogin.CancellationPending)
{
if (!connection_test.IsSIPServerAvailable())
{
// Update label
if (this.lblRegistering.InvokeRequired)
{
this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "Server unavailable");
}
else
{
this.lblRegistering.Text = "Server unavailable";
}
// Failed attemp
this.bgwProcessLogin.CancelAsync();
e.Cancel = true;
return;
}
// Report current progress
this.bgwProcessLogin.ReportProgress(1, "Server available");
}
else
{
// User cancelled
e.Cancel = true;
return;
}
.
.
.
}
private void bgwProcessLogin_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Check for any errors
if (e.Error == null)
{
if (e.Cancelled)
{
// User cancelled login or login failed
}
else
{
// Login completed successfully
}
}
else
{
// Something failed display error
this.statusDisplay1.CallStatus = e.Error.Message;
}
}
private void bgwProcessLogin_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.lblRegistering.Text = e.UserState.ToString();
}
private void btnCancel_Click(object sender, EventArgs e)
{
// Cancel the logging in process
this.bgwProcessLogin.CancelAsync();
this.lblRegistering.Text = "Logged out";
}
There is maybe only one problem: if one of the operation in DoWork event handler would last for a long time. In this case you could abort your pending operation ONLY after that operation finished. If all operations in DoWork event can't last very long (for instance, no more than 5 seconds), its all OK, but if one of the operations can last for long time (5 minutes, for instance) in this case user have to wait until this operation finished.
If DoWork contains long lasting operations you can use something like AbortableBackgroundWorker. Something like this:
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
In this case you can truly abort pending operations, but you also have some restrictions (for more information about aborting managed thread and some restrictions see Plumbing the Depths of the ThreadAbortException Using Rotor).
P.S. I agree with Oliver that you should wrap InvokeRequired in more usable form.
You are doing it the right way, I believe. You will find thread members that allow you to terminate or abort a thread, but you don't want to use them for something like this. It might look a little weird to have all of the "cancelled" checks in your code, but that allows you to control exactly when you exit your thread. If you were to "rudely" abort the worker thread, the thread has no control of when it exits, and there could be corrupted state.
Within your DoWork() function you wrote .... Depending on how many tasks of the same structure are coming like the displayed two one, you could refactor this structure into an own method, giving the changing parts as parameters.
Also this InvokeRequired if-else branch has doubled the output string. A little search here on stackoverflow or on the web should show you a pattern to accomplish this doubling.
Evernything else looks quite good.
There is one thing I don't need to call the this.bgwProcessLogin.CancelAsync(); as you can just set this e.Cancel = true;
Related
I am developing a skype-like application, I have an external DLL that do most of the work and fires events handled in my class ip2ip, one of this events is incoming_call fired when there is an incoming call as the name suggest. I'm trying to manage missed calls.
Now this is the relevant part of the code in this class:
private void ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = null;
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
while (AcceptIncomingCall.HasValue == false) Thread.Sleep(100);
if(AcceptIncomingCall.Value == true)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall = null;
IncomingCall = false;
}
}
IncomingCall is a property generating a PropertyChangedEvent, wich is captured in my main class where I have this code:
private void ip2ip_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e != null && string.IsNullOrEmpty(e.PropertyName) == false)
{
..............
if (e.PropertyName.Equals("IncomingCall") && ip2ip.IncomingCall == true)
{
Invoke(new MethodInvoker(delegate
{
pnlCalling.Visible = true;
aTimer.Start();
}));
}
................
}
}
public Form1()
{
.......
aTimer = new System.Windows.Forms.Timer();
aTimer.Interval = 10000;
aTimer.Tick += aTimer_Tick;
}
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = false;
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = true;
}
I need the timer to manage the missed call, when there is an incoming call a panel appears, with buttons to accept/reject the call. If the user waits too much the call is considered rejected (missed).
In this way it doesn't work, probably I'm doing something wrong with the timer, as without any timer everything works. I also tried the timer of the class System.Timers with same results. Any Idea?
EDIT
This is my expectation, there is an incoming call so the event ics_IncomingCall is fired, IncomingCall=true cause the execution to go to the main class (we are still in same thread, I see it debugging step by step in VS) where is invoked in the GUI thread the panel to be visible and started the timer, now we have one thread where a while loop block the execution until in the other thread user do something (accept/reject).
The problem exist when the user accept the call, the code after the while loop is always executed, the caller has no problem at all and receive the stream, but in the receiver (who receive the stream as I verified in wireshark) the DLL (who is responsible to show the incoming video) fails to do its job for some reason unknown to me but caused by the timer.
It is unfortunate your question does not include a good, minimal, complete code example that reliably reproduces the problem. Having such a code example would make it much more practical for someone to provide a useful answer.
That said, as explained by commenter varocarbas, your fundamental problem appears to be that you have blocked the UI thread (with the while loop), while at the same time hoping for the UI thread to handle other activity (such as the timer's tick event). In fact, you are also preventing the button click from having an effect. The button Click event handlers can't execute either, while the UI thread is blocked.
One possible way to fix this would be to use a TaskCompletionSource<T> to provide the ics_IncomingCall() with a waitable object, which the buttons and timer can use to signal. For example:
// Change from "bool?" to this:
private TaskCompletionSource<bool> AcceptIncomingCall;
public void HandleCall(bool accept)
{
AcceptIncomingCall.SetResult(accept);
}
private async Task ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = new TaskCompletionSource<bool>();
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
if (await AcceptIncomingCall.Task)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall.Dispose();
IncomingCall = false;
}
}
and:
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
This causes the ics_IncomingCall() method to return when it reaches the await statement, allowing its thread to continue executing. The button Click event handlers will call back to the public method that encapsulates your field (public fields are very dangerous and should be avoided in almost all situations), setting the result value for the TaskCompletionSource object that is being awaited.
Once the result value has been set, this will cause the framework to resume executing your ics_IncomingCall() method where it left off, but now with the value returned from the button Click event handlers. I.e. true if the user clicked the btnOk and false if they clicked btnNo or the timer interval elapsed.
Note that this changes the signature of your ics_IncomingCall() method, which will force a change to the caller. The best way to handle that will be to change the caller as well, to be async and to use await ics_IncomingCall(...). That will of course force a change in its caller, and its caller's caller, and so on. But you need to release the UI thread, and this is the best way to do it. Hopefully you don't have a lot of callers to change, but even if you do, this is the way to go.
If the above does not seem to address your problem, please provide a good MCVE. Note that a good MCVE is both complete and minimal. You will want to remove from the example any code that is not strictly required to reproduce the problem. At the same time, make sure someone can copy and paste the code into an empty project and have it run with at most very minimal effort, and preferably none at all.
I want to abort the process but not able to do so, I am using Background worker with my functions of processing.
public void Init()
{
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
if (bw.CancellationPending == true)
{
e.Cancel = true;
}
else
{
e.Result = abd();
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
lbltext.content="Canceled";
}
else
{
lbltext.content="Completed";
}
}
private void btncan_Click(object sender, RoutedEventArgs e)
{
bw.CancelAsync();
}
private void btnstart_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
}
I am not able to abort the process using this code.
Function abd() is performing the processing part and returning the result.
Please provide me any solution.
Thanks.
When you call bw.CancelAsync() you just set CancellationPending flag to true. It does not cancels something by default. You need to handle pending cancellation manually. But you can't do that with your code, because when you click button, there are three possible options:
Long-running abd() method finished it's work and there is nothing to cancel
abd() started it's work, and background worker is blocked - it's waiting for results of abd(), then it continues execution - i.e. exits if-else block and raises RunWorkerCompleted event.
Nearly impossible option - you will be fast as light, and you will click button before if-else block entered. Than CancellationPending will be true, and abd() will not start execution
If you want to use cancellation, then do your long-running task in a loop, and check if cancellation is pending on each step:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
List<Foo> results = new List<Foo>();
// any loop here - foreach, while
for(int i = 0; i < steps_count; i++)
{
// check status on each step
if (bw.CancellationPending == true)
{
e.Cancel = true;
return; // abort work, if it's cancelled
}
results.Add(abd()); // add part of results
}
e.Result = results; // return all results
}
Probably DoWork may have finished its work before calling CancelAsync and as mentioned in the docs e.Cancelled may be false..
Docs say this
Be aware that your code in the DoWork event handler may finish its
work as a cancellation request is being made, and your polling loop
may miss CancellationPending being set to true. In this case, the
Cancelled flag of System.ComponentModel.RunWorkerCompletedEventArgs in
your RunWorkerCompleted event handler will not be set to true, even
though a cancellation request was made. This situation is called a
race condition and is a common concern in multithreaded programming.
How about the following?
While(!bw.CancellationPending)
{
//do some work!
}
e.Cancel = true;
In my WPF application, I have a long running process which converts files to PDFs using BlueBeam Q Server. When the process takes place, it should not freeze, so the below code has written to take care of that:
private void btn_convert_Click(object sender, RoutedEventArgs e)
{
thread = new Thread(new ThreadStart(WorkerMethod));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
thread.Start();
}
WorkerMethod()
{
//code to connect to Q server and conversion goes here
}
Now, when the process starts, a cancel button will be visible to the user. When the user presses cancel, I want to abort the thread started. I wrote the code as below:
private void btn_cancel_Click(object sender, RoutedEventArgs e)
{
if (thread.Name == "PDF")
thread.Abort();
}
But the thread doesn't abort and continues the process. Please give me your valuable suggestions.
You should avoid Abort whenever possible. Search SO for how to cancel threads gracefully - which can not be done when the thread code calls other code you can not influence, like a third party library method or something like that.
For example if your thread method does something like this:
WorkerMethod()
{
CallFunctionInExternalDLL();
}
it can not be aborted properly.
To "cancel" such a thread, it's best to indicate to the thread it should cancel (using a bool flag, for example) and have the thread roll back its result (for example, delete a created PDF or things like that). Your application could then just continue as if the thread had never been started.
For example your code could then look like this:
WorkerMethod()
{
CallFunctionInExternalDLL();
if (m_threadAborted)
RollBackWhatFunctionDid();
}
If your thread looks like this:
WorkerMethod()
{
while (true)
{
CallFunctionInExternalDLL();
}
}
You could do this:
WorkerMethod()
{
while (!m_threadAborted)
{
CallFunctionInExternalDLL();
}
if (m_threadAborted)
RollBackStuff();
}
In these examples, m_threadAborted is a bool flag declared like this:
private volatile bool m_threadAborted = false;
You can use Backgroundworker instead of Thread, and you will be able to cancel it if you code include a loop (you have to test a Boolean property in each loop)
// define the backgroundWorker
this.backgroundWorker.DoWork += new DoWorkEventHandler(this.BackgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BackgroundWorker_RunWorkerCompleted);
this.backgroundWorker.WorkerSupportsCancellation = true;
// execute process
this.backgroundWorker.RunWorkerAsync();
// cancel process
this.backgroundWorker.CancelAsync();
and in your code of BackgroundWorker_DoWork :
// in a loop
if (this.backgroundWorker.CancellationPending == false)
{
...
}
You could use the CancellationTokenSource as described here:
http://msdn.microsoft.com/en-us/library/dd997364.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
static void CancelWithThreadPoolMiniSnippet()
{
//Thread 1: The Requestor
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
// Pass the token to the cancelable operation.
ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
// Request cancellation by setting a flag on the token.
cts.Cancel();
}
//Thread 2:The Listener
static void DoSomeWork(object obj)
{
CancellationToken token = (CancellationToken)obj;
for (int i = 0; i < 100000; i++)
{
// Simulating work.
Thread.SpinWait(5000000);
if (token.IsCancellationRequested)
{
// Perform cleanup if necessary.
//...
// Terminate the operation.
break;
}
}
}
This post describes other possible ways:
Question about terminating a thread cleanly in .NET
I have a backgroundworker that runs a single process. I want to be able to cancel the processing while it's going, but when I call the CancelAsync() method, it never actually cancels. Where am I wrong?
Here's the DoWork() method:
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker b = sender as BackgroundWorker;
if (b != null)
{
if (!b.CancellationPending)
{
try
{
// Let's run the process as a backgroundworker so we have the ability to cancel the search, and/or be able to view results while it's still searching
ProcessParameters pp = e.Argument as ProcessParameters;
if (pp.DoReplace)
results = FindReplace.FindReplace.FindAndReplace(pp.PathToSearch, pp.FindText, pp.ReplaceText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
else
results = FindReplace.FindReplace.Find(pp.PathToSearch, pp.FindText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
// Cancel was clicked
e.Cancel = true;
}
}
}
Here's the method that starts the processing:
private void btnGo_Click(object sender, EventArgs e)
{
if (btnGo.Text == "Cancel")
{
if (DialogResult.Yes == MessageBox.Show("Are you sure you wish to cancel?", "Cancel Requested", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
bgw.CancelAsync();
return;
}
if (tbFind.Text.Length == 0)
{
MessageBox.Show("Find text is not valid.");
return;
}
tbFound.Text = String.Empty;
tbFoundInThisFile.Text = String.Empty;
lvResults.Items.Clear();
includeList = null;
excludeList = null;
results = null;
if (radDirectory.Checked && !radFile.Checked)
{
includeList = BuildIncludeExcludeList(tbIncludeFiles.Text);
excludeList = BuildIncludeExcludeList(tbExcludeFiles.Text);
}
ProcessParameters pp = null;
if (chkReplace.Checked)
pp = new ProcessParameters(tbPath.Text, tbFind.Text, tbReplace.Text, chkUseRegEx.Checked, includeList, excludeList, chkRecursion.Checked, chkIgnoreCase.Checked, true);
else
pp = new ProcessParameters(tbPath.Text, tbFind.Text, chkUseRegEx.Checked, includeList, excludeList, chkRecursion.Checked, chkIgnoreCase.Checked, false);
bgw.RunWorkerAsync(pp);
// Toggle fields to locked while it's running
btnGo.Text = "Cancel";
}
And here's the WorkerCompleted() event:
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnGo.Text = "Go";
string message = String.Empty;
const string caption = "FindAndReplace is Complete";
if (!e.Cancelled)
{
if (results != null)
{
tbFound.Text = results.Found.ToString();
tbSearched.Text = results.FilesSearched.ToString();
tbSkipped.Text = results.FilesSkipped.ToString();
message = String.Format("Search finished resulting in {0} match(es).", results.Found);
}
else
message = "The FindAndReplace results were empty. The process was cancelled or there was an error during operation.";
}
else
message = "The FindAndReplace process was cancelled.";
if (e.Error != null)
message += String.Format("{0}{0}There was an error during processing: {1}", Environment.NewLine, e.Error);
MessageBox.Show(message, caption);
}
CancelAsync doesn't actually abort your thread or anything like
that. It sends a message to the worker thread that work should be
cancelled via BackgroundWorker.CancellationPending. Your DoWork
delegate that is being ran in the background must periodically check
this property and handle the cancellation itself.
Read more here
You don't really have a way to cancel the operation. The problem is that this code
if (pp.DoReplace)
results = FindReplace.FindReplace.FindAndReplace(pp.PathToSearch, pp.FindText, pp.ReplaceText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
else
results = FindReplace.FindReplace.Find(pp.PathToSearch, pp.FindText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
doesn't have any way to break once it starts running. So, what winds up happening is that you hit cancel, but the cancel never registers unless you've canceled before the action begins. Once that action is complete, the DoWork method returns successfully and the backgroundworker never triggers the cancellation.
EDIT: If you have a way to break the text up into smaller chunks that can then be "searched and replaced", you could loop through those segments and perform a cancellation check on each loop. You'd need to make sure that you account for the search string being across those break boundaries, though, so it may actually take LONGER to allow for cancellation.
Your code is right, but if you carefully read it again, you will see that once the background worker starts, soon it goes beyond the cancel check. After that even if you try to cancel, it won't work any more.
You have to redesign your search and replace algorithm to include the cancel check too, so as to support cancellation as you wished.
I have a method processData() that takes a large amount of data and does some work on it. There's a start button that initiates the processing. I need a cancel button that stops the processing wherever it's at. How can I implement something like that? The thing I don't get is how to make the cancel button usable once the processing has started since the rest of the UI is frozen when the function is running.
BackgroundWorker.CancelAsync Method is what you need. Here is a good example for you.
If you have got a time consuming process you will have to use a separate thread to handle that in order to support for cancellation. If you execute that time consuming process in the main thread(UI thread) it will be busy and won't take your cancellation request in to account until it finish that task. That's why you experience UI freezing.
If you use a backgroundWorker for your time consuming task and if you check the CancellationPending flag in the BackgroundWorker.DoWork method you could achieve what you want.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace BackgroundWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//mandatory. Otherwise will throw an exception when calling ReportProgress method
backgroundWorker1.WorkerReportsProgress = true;
//mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
backgroundWorker1.WorkerSupportsCancellation = true;
}
//This method is executed in a separate thread created by the background worker.
//so don't try to access any UI controls here!! (unless you use a delegate to do it)
//this attribute will prevent the debugger to stop here if any exception is raised.
//[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//NOTE: we shouldn't use a try catch block here (unless you rethrow the exception)
//the backgroundworker will be able to detect any exception on this code.
//if any exception is produced, it will be available to you on
//the RunWorkerCompletedEventArgs object, method backgroundWorker1_RunWorkerCompleted
//try
//{
DateTime start = DateTime.Now;
e.Result = "";
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(50); //do some intense task here.
backgroundWorker1.ReportProgress(i, DateTime.Now); //notify progress to main thread. We also pass time information in UserState to cover this property in the example.
//Error handling: uncomment this code if you want to test how an exception is handled by the background worker.
//also uncomment the mentioned attribute above to it doesn't stop in the debugger.
//if (i == 34)
// throw new Exception("something wrong here!!");
//if cancellation is pending, cancel work.
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
TimeSpan duration = DateTime.Now - start;
//we could return some useful information here, like calculation output, number of items affected, etc.. to the main thread.
e.Result = "Duration: " + duration.TotalMilliseconds.ToString() + " ms.";
//}
//catch(Exception ex){
// MessageBox.Show("Don't use try catch here, let the backgroundworker handle it for you!");
//}
}
//This event is raised on the main thread.
//It is safe to access UI controls here.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage; //update progress bar
DateTime time = Convert.ToDateTime(e.UserState); //get additional information about progress
//in this example, we log that optional additional info to textbox
txtOutput.AppendText(time.ToLongTimeString());
txtOutput.AppendText(Environment.NewLine);
}
//This is executed after the task is complete whatever the task has completed: a) sucessfully, b) with error c)has been cancelled
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) {
MessageBox.Show("The task has been cancelled");
}
else if (e.Error != null)
{
MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());
}
else {
MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());
}
}
private void btoCancel_Click(object sender, EventArgs e)
{
//notify background worker we want to cancel the operation.
//this code doesn't actually cancel or kill the thread that is executing the job.
backgroundWorker1.CancelAsync();
}
private void btoStart_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
}
}
Use a BackgroundWorker.
Put the heavy code in the DoWork event.
The cancel button should call CancelAsync on the BackgroundWorker.
In the heacy code in DoWork check the CancellationPending property periodically. If the property is true you should abort the work.
stops the processing wherever it's at
If you mean that the process should stop immediately and not even wait for a moment where it checks a cancellation token, you may consider running the process in a separate AppDomain and kill it when you cancel.
Although this is perfectly possible, I would recommend a controlled termination as in the other answers, especially when your process changes external state.