How do I cancel the execution of the.net background thread

  • 2020-05-17 05:20:46
  • OfStack

introduce
One of the most common problems we encounter when programming with the multithreaded model is that when we close the foreground UI thread, the background worker thread is still active, causing the entire application to fail. At this point, we need a safer way to terminate the background thread, so that we can terminate the background thread at any time, and do the corresponding resource cleaning when the thread ends (such as writing the memory data to the hard disk). The.net framework provides tools to do this.

directory
IsBackground properties
Abort method
Round robin mode
Unblock the thread
IsBackgound properties
The Thread class provides the IsBackground property, and when the IsBackground property of a thread is set to true, it indicates that the thread is a background worker thread. When an application ends, all its background threads are automatically terminated. If you have a background thread listening for an Socket connection and it is blocking, it is appropriate to set the IsBackground property of the thread to True and have it automatically end with the end of the application. But in this case, the thread will end quietly, it will not throw any exceptions, your thread will not have a chance to execute 1 needed clean up code. For example, data in memory may be written to disk too late, resulting in lost data.

Abort method
You can call the Abort method of the Thread class to force the termination of the thread. When this method is called on, ThreadAbortException is thrown on the thread and led to thread termination. By catching this exception, you can perform some resource cleanup code. However, there are some problems with this pattern, mainly that it is difficult to know where the code on the thread is executing, and it is also difficult to write all the corresponding resource cleaning code. In general, this is a rough way to terminate thread execution and is generally not recommended.

Round robin mode
If the background thread is going to perform a long computation, you can break the computation into small segments and check frequently to see if you need to cancel the thread. The.NET framework provides the CancellationTokenSource class as a unified 1 mode for thread cancellation. Such as:
 
public class Example 
{ 
public static void Main() 
{ 
CancellationTokenSource cts = new CancellationTokenSource(); 
var thread = new Thread(ThreadWork); 
thread.Start(cts.Token); 
while (true) 
{ 
if(Console.ReadKey().KeyChar == 'c') 
{ 
Console.WriteLine(" Request to cancel thread execution "); 
cts.Cancel(); 
break; 
} 
} 
Console.ReadLine(); 
} 

private static void ThreadWork(object state) 
{ 
CancellationToken cancellationToken = (CancellationToken)state; 

while (true) 
{ 
//  Check whether to cancel  
if(cancellationToken.IsCancellationRequested) 
{ 
Console.WriteLine(" The thread has been canceled "); 
Console.WriteLine(" The thread's resources have been cleaned up. "); 
break; 
} 
//  Simulation work  
Thread.SpinWait(500000); 
Console.WriteLine(" I'm still working. "); 
} 
} 
} 

Unblock the thread
In the example above, the background thread takes a long time to compute, but more often than not, the thread gets blocked waiting for an event. At this point, the thread is effectively no longer executing, and it obviously has no chance to check the cancel flag. So how do you solve this problem? The WaitHandle property of CancellationToken provides the solution. The WaitHandle class has one static method, WaitAny, which can wait for multiple events at the same time. When any one of these events is valid, the thread returns from the blocking state. You can use the return value of the WaitAny method to determine what happened and execute the code accordingly. Example:
 
public class Example 
{ 
private static int Value; 

public static void Main() 
{ 
var autoResetEvent = new AutoResetEvent(false); 
var cts = new CancellationTokenSource(); 
var state = new { ValueAvailableEvent = autoResetEvent, CancellationToken = cts.Token }; 
var threadConsumer = new Thread(ConsumerThreadWork); 
var threadProducter = new Thread(ProducterThreadWork); 

threadConsumer.Start(state); 
threadProducter.Start(state); 

while (true) 
{ 
if (Console.ReadKey().KeyChar == 'c') 
{ 
Console.WriteLine(" Request to cancel thread execution "); 
cts.Cancel(); 
break; 
} 
} 
Console.ReadLine(); 

} 
public static void ProducterThreadWork(dynamic state) 
{ 
var valueAvailableEvent = (AutoResetEvent)state.ValueAvailableEvent; 
var cancellationToken = (CancellationToken)state.CancellationToken; 
var rand = new Random(); 
while (!cancellationToken.IsCancellationRequested) 
{ 
Value = rand.Next(); 
Console.WriteLine("\r\n produce 1 A value {0}", Value); 
valueAvailableEvent.Set(); 
Thread.Sleep(500); 
} 

Console.WriteLine(" The producer thread is canceled. "); 
} 

public static void ConsumerThreadWork(dynamic state) 
{ 
var valueAvailableEvent = (AutoResetEvent)state.ValueAvailableEvent; 
var cancellationToken = (CancellationToken)state.CancellationToken; 
var events = new[] { valueAvailableEvent, cancellationToken.WaitHandle }; 

while (true) 
{ 
var eventIndex = WaitHandle.WaitAny(events); 
//  Process the data  
if (eventIndex == 0) 
{ 
Console.WriteLine(" Processing value {0} . ", Value); 
} 
//  Handling cancellation events  
else if (eventIndex == 1) 
{ 
Console.WriteLine(" The consumer thread is cancelled. "); 
break; 
} 
} 
} 
} 

In the example above, there are three threads, the UI thread, the producer thread, and the consumer thread. Where the producer thread generates a valid value every 1 second and stores the data in the Value field, the consumer thread waits for the value to be generated, and the waiting process is blocked. The consuming thread waits for the value valid event or cancels the event at the same time through the WaitHandle.WaitAny method. When any one event is valid, the thread will continue and judge the event by the return value and do the corresponding processing.

conclusion
The problem of thread cancellation in multithreaded models is complex. The Thread.IsBackground property provides a way to automatically terminate a thread after the foreground thread ends. The Thread.Abort method provides a "rough" way to end a thread. The CancellationTokenSource class is the standard mode for thread cancellation and should be used more often. The article does not write much, is basically the word is not enough, the code to gather together, the big guy will have a look.

Related articles: