What is the most efficient way to create a “cancel” event in a C# program that is crunching a large set of data in a loop on a separate thread?
For now, I am simply using a cancel event that is triggered from my UI thread, which subsequently calls an “onCancel” function on the number crunching thread. That cancel functio开发者_开发问答n sets a variable to “true”, which the crunch loop checks periodically, e.g.
Class Cruncher {
private bool cancel = false;
public cruncher()
{
crunch();
}
private void crunch()
{
while(conditions AND !cancel) { crunch; }
dispose_resources;
}
private void onCancel()
{
cancel = true;
}
}
While I am not checking the cancel variable as often as my example above (and not actually performing a NOT cancel), I would still like to optimize this crunch method as much as possible. Any examples where this is done more efficiently would be very nice to see.
The cancel event/flag should be a volatile
... I asked a very similar question to yours: Is it safe to use a boolean flag to stop a thread from running in C#
I would also recommend that when you cancel your threads you wait for all of them to cancel by using something similar to the C# version of CountDownLatch. It's useful when you want to guarantee that the thread is canceled.
It will ultimately always result in something like this - although it's important that you make your cancel
variable volatile, as otherwise the worker threads may not see the change from the cancelling thread.
You've got to check something periodically unless you want to go the more drastic route of interrupting the thread (which I don't recommend). Checking a single Boolean flag isn't likely to be exactly costly... if you can do a reasonable chunk of work in each iteration of the loop (enough to dwarf the cost of the check) then that's fine.
If you ever need to perform any waiting, however (in the worker thread), then you may be able to improve matters, by using a form of waiting (e.g. Monitor.Wait
) which allows the cancelling thread to wake any waiting threads up early. That won't make normal operation more efficient, but it will allow the threads to terminate more quickly in the event of cancellation.
Especially since it's UI-triggered, I would recommend just leveraging the BackgroundWorker that's already in the framework, especially since it'll nicely have the progress and done events happen on the UI thread for you (so you don't have to invoke it over yourself).
Then you can just use the CancelAsync() call. Admittedly, it's not much different than what you're already doing, just done in the framework already (and including the thread synchronization logic)
As Jon mentioned, you're still going to want to do cooperative cancellation (checking CancellationPending in your DoWork for use of BackgroundWorker) since the 'interrupt/abort the thread' option is something you want to avoid if possible.
If in .NET 4 you can use TPL and the new Cancellation support, but again it's focused on cooperative cancellation.
I recommend using the unified cancellation model that was introduced in .NET 4.0 (if .NET 4.0 is an option).
It is very efficient, and allows integrated cancellation with Task
objects and Parallel LINQ.
i would do it the same way. i would also add Thread.Sleep in to the loop to yield control to the main thread.
http://msdn.microsoft.com/en-us/library/7a2f3ay4%28VS.80%29.aspx
精彩评论