How to terminate Parallel.ForEach threads immediately? - c#

I am calling some code in Parallel.Foreach(). The code has Thread.Sleep(60000), so if I cancel the token also then it waits for 60 seconds before cancelling the Parallel.ForEach loop.
Thread.Sleep() is put in this code for explanation. The actual code has some code that waits for other resources.
I want to cancel all activity as cts.Cancel(); is called.
I tried LoopState also but it will not work in my case.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
int[] nums = Enumerable.Range(0, 10000000).ToArray();
CancellationTokenSource cts = new CancellationTokenSource();
// Use ParallelOptions instance to store the CancellationToken
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
Console.WriteLine("Press any key to start. Press 'c' to cancel.");
Console.ReadKey();
// Run a task so that we can cancel from another thread.
Task.Factory.StartNew(() =>
{
if (Console.ReadKey().KeyChar == 'c')
{
cts.Cancel();
}
Console.WriteLine("press any key to exit");
});
try
{
Parallel.ForEach(nums, po, (num) =>
{
double d = Math.Sqrt(num);
Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(60000); //Thread Sleep for 1 minute
po.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
finally
{
cts.Dispose();
}
Console.ReadKey();
}
}

If I understand correctly, it is not really that you need to interrupt the thread immediately, but rather that you simply want to be able to interrupt the Sleep() method while it's waiting.
There are a number of options, but one of the simplest IMHO is to use the CancellationToken.WaitHandle property value, and wait on that instead of sleeping:
po.CancellationToken.WaitHandle.WaitOne(60000);
If the token is signaled, the Wait() method will return before the timeout specified (one minute in this case).
Typically you'd check the return value, so you can tell the difference between the handle being signaled and the wait timing out, but in your case you immediately call po.CancellationToken.ThrowIfCancellationRequested();, so it seems reasonable to me to just ignore the return value from the Wait() method and let the next program statement take care of actually interrupting the method.

Related

CancellationToken blocks thread execution

I'm trying to use cancellation tokens to break out of a loop from another thread:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationTokenTest
{
class Program
{
private static CancellationTokenSource token;
static async Task Main(string[] args)
{
token = new CancellationTokenSource();
var t1 = LongProcess1();
for (int i = 0; i < 5; i++)
{
try
{
await Task.Delay(2000, token.Token);
Console.WriteLine($"Main awaited {i}");
}
catch (OperationCanceledException)
{
break;
}
}
Console.WriteLine("Main loop completed");
t1.Wait();
Console.WriteLine("Application end");
}
static async Task LongProcess1()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine($"LongProcess1 awaited {i}");
}
Console.WriteLine("Submitting cancel");
token.Cancel(); // <------ Blocking execution
Console.WriteLine("Cancellation done!");
}
}
}
The problem is that the line Console.WriteLine("Cancellation done!"); never gets executed, and the Main() method keeps waiting for t1 to complete.
I do not understand why!
I ran this on .NET 5, but it doesn't seem to be framework dependent.
Can someone help me understand what's going on?
This deadlock is almost exactly like the one described on my blog. One thing you need to be aware of is that CancellationTokenSource.Cancel in this case is running the cancellation token continuations on the thread that calls Cancel.
So, here's what's happening:
Main starts LongProcess.
LongProcess uses some awaits and resumes on a thread pool thread.
In the meantime, Main is doing an await Task.Delay.
When LongProcess does the Cancel, it actually resumes executing the Main method on the current thread. You can verify this by placing a breakpoint in your catch block and looking at the call stack.
The current thread (a thread pool thread) then blocks on the task (.Wait()).
Now, LongProcess cannot complete because the thread currently running it is blocked waiting for it to complete.
To fix it, change the Wait() to an await.
The problem is the t1.Wait(); (Wait() is almost always the problem ;p) - this is stealing the thread. Use await t1; instead, and everything will work. Effectively, the cancellation needs to invoke some callbacks (the continuations in the cancellation state) and then return to whatever did the cancellation, and you've inserted a blockage into the cancellation.

Running two tasks with Parallel.Invoke and add a timeout in case one task takes longer

I'm calling two functions that rely on some external web services. For now, they run in parallel and when they both complete the execution resumes. However, if the external servers take too much time to process the requests, it could lock my code for a while.
I want to add a timeout so that if the servers take more than 10 seconds to respond then just continue on. This is what I have, how can I add a timeout?
Parallel.Invoke(
() => FunctionThatCallsServer1(TheParameter),
() => FunctionThatCallsServer2(TheParameter)
);
RunThisFunctionNoMatterWhatAfter10Seconds();
I don't think there's an easy way of timing out a Parallel.Invoke once the functions have started, which clearly they will have done after ten seconds here. Parallel.Invoke waits for the functions to complete even if you cancel, so you would have to find a way to complete the functions early.
However, under the covers Parallel.Invoke uses Tasks, and if you use Tasks directly instead of Parallel.Invoke then you can provide a timeout. The code below shows how:
Task task1 = Task.Run(() => FunctionThatCallsServer1(TheParameter));
Task task2 = Task.Run(() => FunctionThatCallsServer2(TheParameter));
// 10000 is timeout in ms, allTasksCompleted is true if they completed, false if timed out
bool allTasksCompleted = Task.WaitAll(new[] { task1, task2 }, 10000);
RunThisFunctionNoMatterWhatAfter10Seconds();
One slight difference this code has with Parallel.Invoke is that if you have a VERY large number of functions then Parallel.Invoke will manage the Task creation better than just blindly creating a Task for every function as here. Parallel.Invoke will create a limited number of Tasks and re-use them as the functions complete. This won't be an issue with just a few functions to call as above.
You will need to create an instance of CancellationTokenSource and right at creating time you ca configure your timeout time, like
var cts = new CancellationTokenSource(timeout);
then you will need to create an instance of ParallelOptions where you set the ParallelOptions.CancellationToken to the token of the CancellationTokenSource, like
var options = new ParallelOptions {
CancellationToken = cts.Token,
};
Then you can call Parallel.Invoke with the options and your actions
try
{
Parallel.Invoke(
options,
() => FunctionThatCallsServer1(token),
() => FunctionThatCallsServer2(token)
);
}
catch (OperationCanceledException ex)
{
// timeout reached
Console.WriteLine("Timeout");
throw;
}
but you will also need to hand the token to the called Server functions and handle the timeout in these actions aswell.
This is because the Parallel.Invoke will only check before it starts an action if the token it got is cancelled. That means if all actions are started before the timeout occures the Parallel.Invoke call will block as long as the actions need to finish.
Update:
A good way to test the cancellation is to define FunctionThatCallsServer1 like,
static void FunctionThatCallsServer1(CancellationToken token) {
var endTime = DateTime.Now.AddSeconds(5);
while (DateTime.Now < endTime) {
token.ThrowIfCancellationRequested();
Thread.Sleep(1);
}
}
Below is the code:
using System;
using System.Threading.Tasks;
namespace Algorithums
{
public class Program
{
public static void Main(string[] args)
{
ParelleTasks();
Console.WriteLine("Main");
Console.ReadLine();
}
private static void ParelleTasks()
{
Task t = Task.Run(() => {
FunctionThatCallsServers();
Console.WriteLine("Task ended after 20 Seconds");
});
try
{
Console.WriteLine("About to wait for 10 sec completion of task {0}", t.Id);
bool result = t.Wait(10000);
Console.WriteLine("Wait completed normally: {0}", result);
Console.WriteLine("The task status: {0:G}", t.Status);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Error: " + e.ToString());
}
RunThisFunctionNoMatterWhatAfter10Seconds();
}
private static bool FunctionThatCallsServers()
{
Parallel.Invoke(
() => FunctionThatCallsServer1(),
() => FunctionThatCallsServer2()
);
return true;
}
private static void FunctionThatCallsServer1()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer1");
}
private static void FunctionThatCallsServer2()
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("FunctionThatCallsServer2");
}
private static void RunThisFunctionNoMatterWhatAfter10Seconds()
{
Console.WriteLine("RunThisFunctionNoMatterWhatAfter10Seconds");
}
}
}

Cancel Thread Sleep

I have a simple Console application with 2 running threads.
The first thread is measuring some values, and the second thread looks for user input and performs some mouse movements.
while (true)
{
if (Input.IsKeyDown(VC_L))
{
Mouse.Move(300, 500);
Thread.Sleep(thread1_delay);
Mouse.Move(670, 300);
Thread.Sleep(thread1_delay);
Mouse.Move(870, 700);
Thread.Sleep(thread1_delay);
}
}
The problem is I want to stop the second thread as soon as I get another key as input. But it's not working since the thread is still sleeping and doesn't react.
Just use a CancellationToken and be done with it
Propagates notification that operations should be canceled.
Example
public static async Task DoFunkyStuff(CancellationToken token)
{
// a logical escape for the loop
while (!token.IsCancellationRequested)
{
try
{
Console.WriteLine("Waiting");
await Task.Delay(1000, token);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Task Cancelled");
}
}
Console.WriteLine("Finished");
}
Usage
static async Task Main(string[] args)
{
var ts = new CancellationTokenSource();
Console.WriteLine("Press key to cancel tasks");
var task = DoFunkyStuff(ts.Token);
// user input
Console.ReadKey();
Console.WriteLine("Cancelling token");
// this is how to cancel
ts.Cancel();
// just to prove the task has been cancelled
await task;
// because i can
Console.WriteLine("Elvis has left the building");
Console.ReadKey();
}
Results
Press key to cancel tasks
Waiting
Waiting
Waiting
Waiting
Waiting
Cancelling token
Task Cancelled
Finished
Elvis has left the building
The second thread should check for a Boolean value when it wakes up. You should set this value to false when your condition is met. Now when the second thread wakes up it will complete it's execution.

Set thread lifetime at startup

Is there a way to set a value for how long a thread should (maximally) be alive when you start the thread?
Said in another way, with "pseudocode", is there anything like this:
Thread t = new Thread();
t.start();
t.abort_after_x_seconds(30);
which would make the thread abort if it lived more than 30 seconds.
Edit: I still can't get it to work, what I originally had is:
while(true)
{
if(...)
{
Thread t = new Thread(new ThreadStart(startMethod));
t.start();
}
Thread.sleep(...);
}
the problem is that sometimes the threads will hang (I'm not implementing what the threads do so I don't know exactly why (it's a school project, we're noobs at organizing)), so I want to kill those threads. I tried using Tasks and CancellationTokens as in the examples below, but when the Task hangs
it can't check if a cancellation request has occured.
Most of the time, you shouldn't be using Threads, use Tasks instead. They are more convenient and more efficient.
Aborting something is not safe, you should use cooperative cancellation instead. If you're calling a method that supports cancellation, then just pass it a cancellation token that will be cancelled after 30 seconds.
So your code could look like this (using .Net 4.5):
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)));
var task = Task.Run(() => YourMethod(cts.Token), cts.Token);
[EDIT: My response was far too slow. But I'll leave this here for the sample code.]
You should use co-operative cancellation for this purpose. The thread itself will need to detect when it should exit, and respond appropriately.
There's a thing called a CancellationToken produced from a CancellationTokenSource that you can use for this purpose.
There's even a CancellationTokenSource constructor which lets you set a timeout.
Here's some sample code to demonstrate its use:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
private void run()
{
using (var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
var task = Task.Run(() => exampleOne(tokenSource.Token));
task.Wait();
}
using (var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
var task = Task.Run(() => exampleTwo(tokenSource.Token));
task.Wait();
}
Console.WriteLine("Done.");
}
static void exampleZero()
{
Console.WriteLine("Starting exampleZero()");
try
{
Thread.Sleep(10000); // Simulate work.
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation cancelled.");
}
Console.WriteLine("Exiting exampleZero()");
}
static void exampleOne(CancellationToken cancellation)
{
Console.WriteLine("Starting exampleOne()");
// Busy loop processing.
while (!cancellation.IsCancellationRequested)
{
// Do some work.
}
Console.WriteLine("Exiting exampleOne()");
}
static void exampleTwo(CancellationToken cancellation)
{
Console.WriteLine("Starting exampleTwo()");
while (!cancellation.WaitHandle.WaitOne(100)) // Wait 100ms between work.
{
// Do some work.
}
Console.WriteLine("Exiting exampleTwo()");
}
static void Main()
{
new Program().run();
}
}
}
As commenters have said, using Abort is bad practice and not guaranteed to abort immediately.
Why would you want to keep the thread alive? The thread will be released back to the pool when the task assigned to it is completed. The next time the task is run on the thread will automatically be given from the pool of threads either by creating another new one or re-using one that is available in the threadpool.
Sounds like your logic/code is bad and needs to be fixed rather than waiting for something for x seconds then terminating it, which in itself will cause knock on problems.
Perhaps you need a timer instead which can tick after 30 seconds then you can disable the timer and kill the task at hand.

Use of IsCancellationRequested property?

What is the use of CancellationToken's IsCancellationRequested property? Consider below code
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Console.WriteLine("Press Enter to Start.\nAgain Press enter to finish.");
Console.ReadLine();
Task t = new Task(() =>
{
int i = 0;
while (true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Task Cancel requested");
break;
}
Console.WriteLine(i++);
}
}, token);
t.Start();
// wait for input before exiting
Console.ReadLine();
tokenSource.Cancel();
if(t.Status==TaskStatus.Canceled)
Console.WriteLine("Task was cancelled");
else
Console.WriteLine("Task completed");
}
I find that on rare occasions code inside if block doesn't run. If so what is the use of polling to see if cancellation is requested?
The problem with your code is that you don't wait for the Task to finish. So, what can happen is this:
You call Cancel().
You check Status, which returns Running.
Confusingly, you write “Task completed” when the Task is still running.
Main() completes, application exits.
(At this point, IsCancellationRequested would be checked from the background thread. But that never happens, since the application already exited.)
To fix that, add t.Wait() after you call Cancel().
But that still won't fix your program completely. You need to tell the Task that it was canceled. And you do that by throwing OperationCanceledException that contains the CancellationToken (the usual way to do that is to call ThrowIfCancellationRequested()).
One issue with that is that Wait()ing on a Task that was canceled will throw an exception, so you will have to catch that.

Categories