Short version:
Use lock()
to synchronize Thread.Abort()
call with critical sections.
Let me explain version:
Generally, when aborting thread's you'd have to consider two types of code:
- Long running code you don't really care if it finishes
- Code that absolutely has to run it's course
First type is the type we don't care about if the user requested abort. Maybe we were counting to a hundred billion and he doesn't care anymore?
If we'd use sentinels such as the CancellationToken
, we would hardly test them in each iteration of the unimportant code would we?
for(long i = 0; i < bajillion; i++){
if(cancellationToken.IsCancellationRequested)
return false;
counter++;
}
So ugly. So for these cases, Thread.Abort()
is a godsend.
Unfortunately, as some say, you can't use Thread.Abort()
because of the atomic code that absolutely has to run! The code has already subtracted money form your account, now it has to complete the transaction and transfer the money to target account. Nobody likes money disappearing.
Luckily, we have mutual exclusion to help us with this sort of thing. And C# makes it pretty:
//unimportant long task code
lock(_lock)
{
//atomic task code
}
And elsewhere
lock(_lock) //same lock
{
_thatThread.Abort();
}
The number of locks will always be <=
number of sentinels, because you'd be wanting sentinels on the unimportant code too (to make it abort faster). This makes the code slightly prettier than the sentinel version, but it also makes aborting better, because it doesn't have to wait for unimportant things.
Also to note is that ThreadAbortException
could be raised anywhere, even in finally
blocks. This unpredictability makes Thread.Abort()
so controversial.
With locks you'd avoid this by just locking the entire try-catch-finally
block. It the cleanup in finally
is essential, then the entire block can be locked. try
blocks are generally made to be as short as possible, one line preferably, so we aren't locking any unnecessary code.
This makes Thread.Abort()
, as you say, a bit less evil. You might not want to call it on the UI thread, though, as you're now locking it.