Question About Terminating a Thread Cleanly in .Net

Question about terminating a thread cleanly in .NET

Unfortunately there may not be a better option. It really depends on your specific scenario. The idea is to stop the thread gracefully at safe points. That is the crux of the reason why Thread.Abort is not good; because it is not guaranteed to occur at safe points. By sprinkling the code with a stopping mechanism you are effectively manually defining the safe points. This is called cooperative cancellation. There are basically 4 broad mechanisms for doing this. You can choose the one that best fits your situation.

Poll a stopping flag

You have already mentioned this method. This a pretty common one. Make periodic checks of the flag at safe points in your algorithm and bail out when it gets signalled. The standard approach is to mark the variable volatile. If that is not possible or inconvenient then you can use a lock. Remember, you cannot mark a local variable as volatile so if a lambda expression captures it through a closure, for example, then you would have to resort to a different method for creating the memory barrier that is required. There is not a whole lot else that needs to be said for this method.

Use the new cancellation mechanisms in the TPL

This is similar to polling a stopping flag except that it uses the new cancellation data structures in the TPL. It is still based on cooperative cancellation patterns. You need to get a CancellationToken and the periodically check IsCancellationRequested. To request cancellation you would call Cancel on the CancellationTokenSource that originally provided the token. There is a lot you can do with the new cancellation mechanisms. You can read more about here.

Use wait handles

This method can be useful if your worker thread requires waiting on an specific interval or for a signal during its normal operation. You can Set a ManualResetEvent, for example, to let the thread know it is time to stop. You can test the event using the WaitOne function which returns a bool indicating whether the event was signalled. The WaitOne takes a parameter that specifies how much time to wait for the call to return if the event was not signaled in that amount of time. You can use this technique in place of Thread.Sleep and get the stopping indication at the same time. It is also useful if there are other WaitHandle instances that the thread may have to wait on. You can call WaitHandle.WaitAny to wait on any event (including the stop event) all in one call. Using an event can be better than calling Thread.Interrupt since you have more control over of the flow of the program (Thread.Interrupt throws an exception so you would have to strategically place the try-catch blocks to perform any necessary cleanup).

Specialized scenarios

There are several one-off scenarios that have very specialized stopping mechanisms. It is definitely outside the scope of this answer to enumerate them all (never mind that it would be nearly impossible). A good example of what I mean here is the Socket class. If the thread is blocked on a call to Send or Receive then calling Close will interrupt the socket on whatever blocking call it was in effectively unblocking it. I am sure there are several other areas in the BCL where similiar techniques can be used to unblock a thread.

Interrupt the thread via Thread.Interrupt

The advantage here is that it is simple and you do not have to focus on sprinkling your code with anything really. The disadvantage is that you have little control over where the safe points are in your algorithm. The reason is because Thread.Interrupt works by injecting an exception inside one of the canned BCL blocking calls. These include Thread.Sleep, WaitHandle.WaitOne, Thread.Join, etc. So you have to be wise about where you place them. However, most the time the algorithm dictates where they go and that is usually fine anyway especially if your algorithm spends most of its time in one of these blocking calls. If you algorithm does not use one of the blocking calls in the BCL then this method will not work for you. The theory here is that the ThreadInterruptException is only generated from .NET waiting call so it is likely at a safe point. At the very least you know that the thread cannot be in unmanaged code or bail out of a critical section leaving a dangling lock in an acquired state. Despite this being less invasive than Thread.Abort I still discourage its use because it is not obvious which calls respond to it and many developers will be unfamiliar with its nuances.

Waiting for and terminating a thread after a given time without blocking in .NET 3.5

If I understood your question correctly, the following algorithm should solve your problem:

  • As before, create a BackgroundWorker to do your background work.

  • In BackgroundWorker_DoWork,

    • create a new thread (let's call it the "third-party thread") to call your third-party library, and then
    • wait for the third-party thread to finish or the timeout to elapse. (*)

That way, your UI won't block, since only the Backgroundworker thread is waiting, not the main thread.

Now about the interesting part: How do you wait for the third-party thread to finish (the step marked with (*))?

My suggestion would be to simply use "loop waiting with sleep", i.e. (pseudo-code, you can use the Stopwatch class for the timeout):

do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms

if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result

It's not best practice, but it's simple, it gets the job done and you can always replace it with fancy cross-thread-syncronization stuff (which is non-trivial to get right) once you got it all working.

How to terminate a thread in C#?

Thread.Abort will "kill" the thread, but this is roughly equivalent to:

Scenario: You want to turn off your computer

Solution: You strap dynamite to your computer, light it, and run.

It's FAR better to trigger an "exit condition", either via CancellationTokenSource.Cancel, setting some (safely accessed) "is running" bool, etc., and calling Thread.Join. This is more like:

Scenario: You want to turn off your computer

Solution: You click start, shut down, and wait until the computer powers down.

Thread.Abort Acceptable Practice?

Checking at specific places in your code if the thread execution needs to stop would generally be preferable, because you have no control over where in the code execution stops using Thread.Abort. That could leave the application in some indeterminate (possibly invalid) state, depending on what the thread does. An added advantage is not having to deal with the ThreadAbortException. More explanation here.

However, from your question I understand that the only reason for aborting the thread would be to start it anew with different parameters. Instead, signal the thread that the parameters have changed, and reuse the same thread. Creating a new thread is a relatively expensive operation and can be avoided in your case.

How to Abort the CLRThread?

Here's a minor example for you

using System;
using System.Threading;

namespace ConsoleApp4
{
class Program
{



static void Main(string[] args)
{
using (CancellationTokenSource source = new CancellationTokenSource())
{
CancellationToken token = source.Token;

token.Register(Notify);

new Thread(Worker).Start(token);

Console.WriteLine("Press a key to abort the thread");
Console.ReadLine();

source.Cancel();

//wait 5 seconds for the thread to abort
source.Token.WaitHandle.WaitOne(5000, true);
}

}

/// <summary>
/// callback to do stuff when thread is cancelled
/// </summary>
static void Notify()
{
Console.WriteLine("thread is cancelled");
}

/// <summary>
/// worker thread
/// </summary>
/// <param name="token"></param>
static void Worker(object token)
{
CancellationToken _token = (CancellationToken)token;

do
{
Console.WriteLine("thread is working....");
Thread.Sleep(1000); //pretend I'm doing work
} while (!_token.IsCancellationRequested);
}

}
}

Revisiting Thread.Abort() - is it safe?

Thread.Abort is a lot safer than it used to be for the following reasons.

  • The runtime will defer aborts while execution is in unmanaged code.
  • The abort will allow finally blocks to execute.

However, there is still a problem with exactly when the ThreadAbortException gets injected. Consider this code.

public class Example
{
private DateTime value = DateTime.MinValue;

public void DoSomething()
{
try
{
value = DateTime.UtcNow;
}
finally
{
}
}
}

If this code were running on a 32-bit platform the value variable could be corrupted if Thread.Abort was called and the ThreadAbortException were injected in the middle of the write to value. Since DateTime is 8 bytes the write has to take place using more than one instruction.

It is possible to guard against this by placing critical code in a finally block and by using Constrained Execution Regions, but it would be incredibly difficult to get right for all but the simplest types your define. And even then you cannot just put everything in a finally block.



Related Topics



Leave a reply



Submit