I have run into a problem that I'm not sure how to resolve. I have a method that contains a call from a service that populates a data table such as:
private void GetCases()
{
try
{
//Setup our criteria
Web_search_criteria myCriteria = new Web_search_criteria()
{
Keynum = 9, //Use Opening Date Key
Range_start = "20100101", //01-01-2010
Range_end = "20121223" //12-23-2013
};
//The myCases object is a datatable that is populated from the GetCasesDT() call.
int status = db.server.GetCasesDT(myCriteria, ref myCases);
}
catch (Exception ex)
{
XtraMessageBox.Show("Unable to get data: " + ex.Message);
}
}
So, as you can see, there isn't a way for me to grab just a few cases at a time - it just grabs it all.
Right now, I have this method being called in a BackgroundWorker's DoWork event, and I display another form with a marquee progress bar on it so the user will know that the system is actually doing something. On that form, I have a Cancel button that I subscribe to. Here is the code that does that:
backgroundWorker1 = new BackgroundWorker()
{
WorkerSupportsCancellation = true,
WorkerReportsProgress = true
};
//DoWork Event
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
//Show the progress bar with subscription to event
pb.btnCancel.Click += this.CancelBW;
pb.Show();
//Run the backgroundworker
this.backgroundWorker1.RunWorkerAsync();
//Don't lock up the application
while (this.backgroundWorker1.IsBusy)
{
Application.DoEvents();
}
I have tried to cancel the BackgroundWorker in the CancelBW event by using CancelAsync(), but then I read more and realize that doesn't work and would only work if I could break up that initial call so the BackgroundWorker could check on progress.
I have thought about using Thread instead of a BackgroundWorker, but have read that Aborting a thread will cause kittens to spontaneously combust.
So, what's the best way for me to handle this in which a user could cancel a long process?
There is no fundamental way to properly cancel this operation. It's not CPU bound work, it's network bound work. You sent the request, you can't exactly pluck that request from the internet. All you can really do is have your code continue executing when your cancel conditions are met instead of waiting around for the operation to finish.
One major issue with your application is the busyloop that you have pumping the message queue:
while (this.backgroundWorker1.IsBusy)
{
Application.DoEvents();
}
This is generally a bad idea, and is a practice that should be avoided. The idea of using a BackgroundWorker
in the first place is for it to be asynchronous; you shouldn't be trying to block the current method until the BGW finishes.
While there are ways of incorporating a BGW into this; this particular case it probably easier to solve using the Task Parallel Library.
var cts = new CancellationTokenSource();
pb.btnCancel.Click += (s, e) => cts.Cancel();
pb.Show();
var task = Task.Factory.StartNew(() => GetCases())
.ContinueWith(t => t.Result, cts.Token)
.ContinueWith(t =>
{
//TODO do whatever you want after the operation finishes or is cancelled;
//use t.IsCanceled to see if it was canceled or not.
});
(I'd also suggest refactoring GetCases
so that it returns the DataTable
that it gets from the DB, rather than modifying an instance field. You can then access it through the Task's Result
.
If you don't want to abort threads (which, indeed, will cause the world to collapse and kittens to combust), the only sane way to end a thread is granularizing the work inside it and perform frequent checks for cancellation tokens (in whichever form you prefer them: built in [for the TPL], messages, locked variables, semaphores, or whatever) and end the thread "cleanly".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With