I have a BackgroundWorker that I call from my main UI thread as follows.
On the MainWindow I declare the BackgroundWorker
private BackgroundWorker backgroundWorkerRefreshFromWeb = new BackgroundWorker();
And in the constructor I set it up as follows.
backgroundWorkerRefreshFromWeb.WorkerReportsProgress = true;
backgroundWorkerRefreshFromWeb.WorkerSupportsCancellation = true;
backgroundWorkerRefreshFromWeb.DoWork +=
new DoWorkEventHandler(backgroundWorkerRefreshFromWeb_DoWork);
backgroundWorkerRefreshFromWeb.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerRefreshFromWeb_RunWorkerCompleted);
backgroundWorkerRefreshFromWeb.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
At the end of the constructor I call the following method to start it on its way.
private void RefreshWebDataTimer(Object state)
{
if (!backgroundWorkerRefreshFromWeb.IsBusy && !backgroundWorkerLoadFromDB.IsBusy)
{
System.Diagnostics.Debug.Print("Refresh Timer Started at {0}", DateTime.Now);
backgroundWorkerRefreshFromWeb.RunWorkerAsync(nfl);
}
}
The DoWork event handler calls a method from another project (DLL). That method has a pattern which calls multiple threads to do process work. when one of those threads throws an error, the application crashed and the BackgroundWorker does not catch it in the RunWorkerCompleted event. The pattern is a complex (probably overly complicated) but is as follows.
In the method the DoWork event handler calls I create a set of "sub-worker" threads in a wrapper as follows...and then WAIT for all threads to finish processing before moving on.
private static void GetRoster(Nfl teams, ref ManualResetEvent[] mre, ref int index)
{
mre = new ManualResetEvent[Dictionary.NUMBER_OF_NFL_TEAMS];
ParseDataAsyncWrapper[] dw = new ParseDataAsyncWrapper[Dictionary.NUMBER_OF_NFL_TEAMS];
index = 0;
foreach (NflTeam team in teams)
{
//Get Roster and create players
mre[index] = new ManualResetEvent(false);
dw[index] = new ParseDataAsyncWrapper(team, mre[index]);
ThreadPool.QueueUserWorkItem(new WaitCallback(dw[index].RosterCallbackAsync), index++);//Don't fully understand this
Thread.Sleep(wait.Next(Dictionary.THREAD_WAIT_MS));
}
foreach (ManualResetEvent re in mre) { if (re != null) { re.WaitOne(); } } //Wait for all threads to finish
mre = null; //allow to be disposed
dw = null;
}
I use the callback for each thread to get a webpage and then process that page:
internal async void RosterCallbackAsync(object State)
{
if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = string.Format("Roster{0}", State); }
WebPage = await Html.WebClientRetryAsync(Dictionary.ROSTER_WEBPAGE.Replace(Dictionary.CITY_REPLACE_STR, this.Team.CityAbr));
Html.ParseRoster(WebPage, Team);
DoneEvent.Set();
}
I am then throwing the exception in Html.ParseRoster but it is not getting caught. This is on a different thread than the BackgroundWorker. I don't know why the BackgroundWorker is not catching it. Since I am waiting for all threads to finish before moving on I don't think the RunWorkerCompleted event would run before I am done here.
I've looked at the help page for Application.DispatcherUnhandledException event and it states:
you will need to write code to do the following: Handle exceptions on
the background thread. Dispatch those exceptions to the main UI
thread. Rethrow them on the main UI thread without handling them to
allow DispatcherUnhandledException to be raised.
My question is 1) Why is the excpetion not being caught? Should I use Application.DispatcherUnhandledException and if so how can I accomplish this? I would ultimately like to throw these exceptions to the BackgroundWorker. Any suggestions or comments would be greatly appreciated.
UPDATE
I have been working on using TPL with await/async and Tasks and updated my code. This has been somewhat successfull as I am now getting the exception back to the BackgroundWorker. Ignoring how I get the exception back to the DoWork event for now ... I check that I am getting an exception by adding a try/catch block and am catching and re-throwing the exception. Here is my DoWork event
private async void backgroundWorkerRefreshFromWeb_DoWork(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
NflStatsComplete = false;
bw.ReportProgress(0, "Starting Data Refresh from Web...");
try
{
e.Result = await Html.RetrieveWebData(bw, e);
}
catch (Exception ex)
{
throw;
}
// If the operation was canceled by the user,
// set the DoWorkEventArgs.Cancel property to true.
if (bw.CancellationPending)
{
e.Cancel = true;
}
}
In the debugger I get an exception and see it thrown. However, when it then goes to the RunWorkerCompleted event the RunWorkerCompletedEventArgs e shows e.Error == null. I don't understand how this can be since I am throwing an exception directly from the DoWork event. Can someone explain this behavior?
The Backgroundworker is setup as an async method. I believe this is causing the RunWorkerCompleted event to fire before the DoWork event has completed and the exception has been raised. By updating the DoWork method to remove the async compiler keyword and removing any awaits the exception propogates back to the method specified by the RunWorkerCompleted event and e.Error != null
Related
I need some help. I started c# and not very familiar with event handling and threading yet. As a beginner and as time and exposure progresses, I would like to learn more on these advanced topics and improved and hope all of you here can help me.
I ran onto this problem of "Cross-thread operation not valid: Control 'textbox control called stackStatus' accessed from a thread other than the thread it was created on". I have tried to troubleshoot this whole day but simply no avail. I am stuck. :-( The program hits an exception and cannot continue to execute smoothly.
I have read the following threads and tried a few things but I guess I am still missing something. Appreciate if someone can help me out here. Thanks.
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on
Here's are most of the portion of the code:
private void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
stackStatus.Refresh();
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
Start_Describestack(); //call describe method to find out the status of cloud creation progress
Task.Delay(12000); // wait 12s in case not ready
Start_Describestack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
private void Start_Describestack()
{
//method making use of timer to call
_timer = new System.Timers.Timer(15000);
_timer.Elapsed += new ElapsedEventHandler(describeStack);
_timer.Enabled = true;
}
delegate void describeStackCallBack(object sender, ElapsedEventArgs e);
private void describeStack(object sender, ElapsedEventArgs e)
{
//this method makes api calls through cloudclient to describe the stack
//this is where the "Cross-thread operation not valid: Control 'stackStatus' accessed from a thread other than the thread it was created on"
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = client.DescribeCloudStacks(request2);
foreach (var stack in response.Stacks)
{
//something is wrong here but I do not know how to fix it. Please help
if (this.stackStatus.InvokeRequired)
{
describeStackCallBack d = new describeStackCallBack(describeStack);
this.Invoke(d, new object[] { sender, e });
stackStatus.Refresh();
describevents();
}
else
{
stackStatus.Text = stack.StackStatus;
stackStatus.Refresh();
describeevents();
}
}
}
catch (Exception)
{
if (this.stackStatus.InvokeRequired)
{
describeStackCallBack d = new describeStackCallBack(describeStack);
this.Invoke(d, new object[] { sender, e });
stackStatus.Text = "Stack not found/Deleted";
}
else
{ stackStatus.Text = "Stack not found/Deleted"; }
}
describeevents();
}
private void describeevents()
{
var newclient = new cloudclient();
var request3 = new eventrequest();
request3.Cloudstackname = stackid;
try
{
var response = newclient.eventstack(request3);
dataGridView3.Rows.Clear();
foreach (var events in response.sevents)
{
dataGridView3.Rows.Add(events.Timestamp, events.ResourceStatus, events.ResourceType);
}
}
catch (Exception)
{
dataGridView3.Rows.Clear();
MessageBox.Show("Stack not ready!");
}
dataGridView3.Refresh();
}
Rather than doing :
stackStatus.Text = "some text";
Try :
stackStatus.Invoke((Action)delegate
{
stackStatus.Text = "some text";
});
Note that GUI element assignment outside the thread or they are declared is deprecated because the controls may no longer be available at any time.
There are two issues in your approach, which conspire to prevent your attempt to imitate the solution to the exception from working:
You have failed to note that the proposed solution calls itself, and in so doing, causes the foreach to be restarted for each time it's invoked from the worker thread.
You are following Microsoft canonical implementation of cross-thread-friendly Invoke()-based code, which IMHO is lame.
It is my opinion that there is no point in ever checking InvokeRequired. The standard pattern always involves situations where on the first entry, you know you will require Invoke(), and even if you didn't, there's no real harm in calling Invoke() when it's not necessary.
Instead, you should always keep separate the code that should run in the UI thread, and the code that does not. Then, in the code that does not, always use Invoke() to execute the code that does.
For example:
private void Start_Describestack()
{
//method making use of timer to call
_timer = new System.Timers.Timer(15000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true;
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
Invoke((MethodInvoker)describeStack);
}
private void describeStack()
{
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = client.DescribeCloudStacks(request2);
foreach (var stack in response.Stacks)
{
stackStatus.Text = stack.StackStatus;
stackStatus.Refresh();
describeevents();
}
}
catch (Exception)
{
stackStatus.Text = "Stack not found/Deleted";
}
describeevents();
}
That said, an improvement on the above would be to use System.Windows.Forms.Timer instead of System.Timers.Timer. The latter raises the Elapsed event on a worker thread, but the former raises its event on the UI thread, right where you want it. No Invoke() required at all.
You have at least one other problem with your code as well:
private void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
stackStatus.Refresh();
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
Start_Describestack(); //call describe method to find out the status of cloud creation progress
Task.Delay(12000); // wait 12s in case not ready
Start_Describestack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
In the above, the call to Task.Delay(12000); accomplishes nothing. The Task.Delay() method doesn't actually block the current thread. Instead, it returns an awaitable task object. The code in which it appears only is delayed if you wait on the returned object.
It's also questionable to call Start_Describestack() twice, because this method doesn't do anything except start the timer. Calling it twice means now you have two timers running.
Finally, you should also not have all those calls to Refresh() in your code. Correctly written Windows Forms code will not need anything like that. Updates to control properties will cause control invalidation automatically, and the control will update as needed at its next opportunity, which as long as the code is written correctly, will be soon enough for the user to not notice any significant delay.
Now, putting all of the above together, it seems to me that you should avoid using the timer altogether. There is still the potential problem that your call to DescribeCloudStacks() is a lengthy one, and could cause the UI to momentarily appear "stuck", which obviously isn't a desirable thing. In addition, the timer-based code, whether you require Invoke() or not, can be harder to understand, especially for someone new to asynchronous programming and threading.
Using the async/await feature, you can write the code in a conventional, procedural way, while still ensuring that the UI remains responsive, and that the UI-related code is always executed in the UI thread where it belongs. That might look something like this:
private async void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
await describeStack(); //call describe method to find out the status of cloud creation progress
await Task.Delay(12000); // wait 12s in case not ready
await describeStack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
private async Task describeStack()
{
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = await Task.Run(() => client.DescribeCloudStacks(request2));
foreach (var stack in response.Stacks)
{
stackStatus.Text = stack.StackStatus;
describeevents();
}
}
catch (Exception)
{
stackStatus.Text = "Stack not found/Deleted";
}
describeevents();
}
The above executes most of the describeStacks() method in the UI thread. The exception would be the DescribeCloudStacks() method call, which is run as a worker task. While it's running, the UI thread is free to operate normally. Execution of the describeStacks() method is temporarily put "on hold" (without blocking the UI thread) while the worker task runs, and then is resumed when it completes.
It's not clear from your original example whether you really wanted a repeating timer or not. The above doesn't use any loops; it calls the describeStack() method only twice, with a 12-second delay in between. But if you want a loop, you can do that as well. Just use the await Task.Delay() for the delay and await describeStack() for the operation, and put that in a loop as you like.
I don't see where the stackStatus object is created so I'm just guessing that you are creating it through a contructor for the class containing describeStack() and then you are registering an event handler for the click. I think what is happening is the event handler is being run on a different thread from the one in which the instance was created so you might have to change how you create the stackStatus object. That error is likely happening because whatever type the stackStatus was created from is known to not be reentrant so when the runtime detects access between threads it raises an exception so you are aware and can either prevent or recover from race-conditions or deadlocks.
I have been looking all over the web at examples like this:
https://msdn.microsoft.com/en-us/library/edzehd2t%28v=vs.110%29.aspx
and all over stackoverflow but I cannot get this to work or find the answer. Or the answer doesn't apply to what I am doing, or there is not enough information in the answer for me to understand how to make this work.
I'm trying to write good code here and avoid Application.DoEvents(). Take a look at the code.
private void btnSendQ_Click(object sender, EventArgs e)
{
//launch a thread from a button click event
Thread ManualQueryThread = new Thread(StartManualQueryThread_Thread);
ManualQueryThread.Priority = ThreadPriority.Normal;
ManualQueryThread.IsBackground = true;
Platform.Log(LogLevel.Debug, "Starting Manual Query Thread.");
Stats.g_CurrentStatus = "Starting Manual Query Thread.";
ManualQueryThread.Start();
}
private void StartManualQueryThread_Thread()
{
try
{
//Here work is performed in the background thread populating objects with data.
}
catch (Exception e)
{
Platform.Log(LogLevel.Error, "Error in StartManualQueryThread(). The error is: " + e.ToString());
}
finally
{
//raise event here since the thread has completed
OnC_FindComplete(EventArgs.Empty);
}
}
public event EventHandler C_FindComplete;
protected virtual void OnC_FindComplete(EventArgs e)
{
//display the gathered information in a datagrid using DisplayC_FindResults(), and enable UI objects.
EventHandler handler = C_FindComplete;
DisplayC_FindResults();
btnSendQ.Enabled = true;
btnMove.Enabled = true;
}
The problem is when a release build is created the datagrid gets a null reference exception and does the red X thing because it is still being updated by a background thread. So how do I properly raise an event in a background thread, and process that event in the UI thread to interact with the UI?
Try to use BackgroundWorker. You'll be able to update your UI in RunWorkerCompleted event.
I have a BackgroundWorker to call a function to do a long process at BackgroundWorker _DoWork, when error occur in the function I will prompt a customized messagebox:
WPFMessageBoxResult result = WPFMessageBox.Show("Activation Fail", "Error!!", WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error);
The exception below happens at WPFMessageBoxResult class :
The calling thread must be STA, because many UI components require this.
Thank you.
You should not try to interact with any UI components from a background thread.
One way could be to catch the exception in your doWork method and assign it to the backgroundworker's result property and then check if that result is a type of exception or not null if you are not using the result for anything else. then check for it in the backgroundWorker_completed event.
BackgroundWorker_DoWork(sender, )
{
try
{
// do work
}
catch (Exception ex)
{
BackgroundWorker w = sender as BackgroundWorker;
if (w != null)
w.Result = ex;
}
}
then
BackgroundWorker_Completed()
{
if (s.Result != null && e.Result is Exception)
{
Exception ex = e.Result as Exception;
// do something with ex
}
}
Usually with Winforms/WPF you use Invoke() to jump onto the UI thread if you need to interact with the UI from a long-running task. You can call invoke from any object that belongs to the UI, but be sure when in the invoke scope to only do as little code as possible. Since this code is on the UI thread it will block/hang the UI if it takes too long.
public void BackgroundWorkerMethod()
{
try
{
// do work
}
catch (Exception e)
{
uiObject.Invoke(new Action(() => {
// now you are on the UI thread
Message.ShowMessage(e.Message);
});
}
}
Your background thread is just a worker thread and not a user interface thread. WPF and WinForms both require that the thread performing user interface actions be marked as STA (Single Threaded Apartment) because the user interface code is not thread safe. They also require that you add a message pump so that windows messages are dispatched.
I recommend that instead of showing the message box in your worker thread you send a message to your main user interface thread and have that thread show the message box. To do this you should pass a reference to the Dispatcher from the main UI thread into the worker thread. Then use the Dispatcher.BeginInvoke to request a delegate be called back on the main thread.
Alternatively you can wait for the background thread to complete and then check the result and show the appropriate answer to the user. Either way the worker thread should not be directly interacting with the user interface.
You must use this method
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
Button btn = new Button();
btn.Width = 100;
btn.Height = 50;
btn.Content = "Test";
myG.Children.Add(btn);
}
));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I'm working with the following components:
a Library (which throws an exception)
a test-console to test my logging
the enterprise library exception handling application blocks
the enterprise library logging application blocks
I'm invoking the library method by using a backgroundworker. The library throws the exception but the RunWorkerCompleted handler is never called.
The only way to catch the exception is to surround my DoWork handler code with a try/catch block.
Did is misunderstand the RunWorkerCompletedEventArgs.Error Property? Isn't it for getting exceptions which got caught by the BackgroundWorker?
Codesample:
static BackgroundWorker w = new BackgroundWorker();
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
w.RunWorkerAsync();
static void w_DoWork(object sender, DoWorkEventArgs e)
{
MyClass m = new MyClass();
w.result = m.Compute();
}
static void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
HandleException(e.Error);
}
/* result related code */
}
static void HandleException(Exception e)
{
ExceptionPolicy.HandleException(e, "MyPolicy");
}
The above sample leads to a termination of my console application. The vs2010 output writes absolutely nothing (only default output).
So where's the problem?
//Edit: this snippet works for catching the library's exception.
static void w_DoWork(object sender, DoWorkEventArgs e)
{
try
{
MyClass m = new MyClass();
w.result = m.Compute();
}catch(Exception e){ }
}
That is the correct pattern for BackgroundWorker.
I suspect the problem is that your Main method is exiting before the BW has completed.
RunWorkerAsync will return immediately and if you are not waiting in Main, then your process will end, perhaps even before the BW has started, never mind completed.
Try adding a Console.ReadLine at the end of your Main method.
Out of interest:
BW behaves differently in a Console app and a Windows app. If you use a WinForms or WPF app, there will be a derived SynchronizationContext on your UI thread and BW will marshal RunWorkerCompleted back to the UI thread and run it there. That's one of the main benefits of BW.
In a Console App, the default SynchronizationContext is used and this marshals RunWorkerCompleted onto a thread pool thread. This means you can block the Main thread and the completed handler will still run.
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.