Set timeout to an operation - c#

I have object obj which is 3rd party component,
// this could take more than 30 seconds
int result = obj.PerformInitTransaction();
I don't know what is happening inside.
What I know is if it take longer time, it is failed.
how to setup a timeout mechanism to this operation, so that if it takes more than 30 seconds I just throw MoreThan30SecondsException ?

You could run the operation in a separate thread and then put a timeout on the thread join operation:
using System.Threading;
class Program {
static void DoSomething() {
try {
// your call here...
obj.PerformInitTransaction();
} catch (ThreadAbortException) {
// cleanup code, if needed...
}
}
public static void Main(params string[] args) {
Thread t = new Thread(DoSomething);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(30))) {
t.Abort();
throw new Exception("More than 30 secs.");
}
}
}

More simply using Task.Wait(TimeSpan):
using System.Threading.Tasks;
var task = Task.Run(() => obj.PerformInitTransaction());
if (task.Wait(TimeSpan.FromSeconds(30)))
return task.Result;
else
throw new Exception("Timed out");

If you don't want to block the main thread you can use a System.Threading.Timer:
private Thread _thread;
void Main(string[] args)
{
_thread = new ThreadStart(ThreadEntry);
_thread.Start();
Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite);
}
void ThreadEntry()
{
int result = obj.PerformInitTransaction();
}
void TimeOut(object state)
{
// Abort the thread - see the comments
_thread.Abort();
throw new ItTimedOutException();
}
Jon Skeet has a less forceful way (Shutting Down Worker Threads Gracefully) of stopping the thread than abort.
However as you're not in control of the operations PerformInitTransaction() is doing there is not much you can do from when Abort fails and leaves the object in an invalid state. As mentioned if you are able to cleanup anything that aborting the PerformInitTransaction has left hanging, you can do this by catching the ThreadAbortException, though as it's a 3rd party call it'll mean guessing the state you've left their method in.
The PerformInitTransaction should really be the one providing the timeout.

The following are two implementations which also throw any exception that happens in the internal task.
For actions (no return value):
public static bool DoWithTimeout(Action action, int timeout)
{
Exception ex = null;
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
try
{
using (cts.Token.Register(Thread.CurrentThread.Abort))
{
action();
}
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
ex = e;
}
}, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (!done)
cts.Cancel();
return done;
}
For Funcs (with return value):
public static bool DoWithTimeout<T>(Func<T> func, int timeout, out T result)
{
Exception ex = null;
result = default(T);
T res = default(T);
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
try
{
using (cts.Token.Register(Thread.CurrentThread.Abort))
{
res = func();
}
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
ex = e;
}
}, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (done)
result = res;
else
cts.Cancel();
return done;
}

I think this is simplest of all:
using System.Threading.Tasks;
var timeToWait = 30000; //ms
Task.Run(async () =>
{
await Task.Delay(timeToWait);
//do your timed task i.e. --
int result = obj.PerformInitTransaction();
});

You need to be careful about aborting an operation like this, especially as it's in a 3rd party component that you (possibly) don't have access to the code to modify.
If you abort the operation then you won't know what state you've left the underlying class in. For example, it may have acquired a lock, and your about has caused that lock to not be released. Even if you destroy the object after aborting the operation it may have altered some state that is global to it and therefore you won't be able to reliably create a new instance without a restart.

You might look at invoking the method in a thread and upon the timeout, abort the thread and raise the exception. Also, you shall have to handle the ThreadBorted Exception in this case.

New approach in .NET 6 / C# 10:
var task = Task.Run(() => SomeMethod(input));
return await task.WaitAsync(TimeSpan.FromSeconds(30));

There is a nice example of a generic solution to this using a helper class here.
It uses the Action delegate to avoid the Thread creation/destruction shown in the previous example.
I hope this helps.

this is what I would use. works similar to how a javascript timeout works.
public class Toolz {
public static System.Threading.Tasks.Task<object> SetTimeout(Func<object> func, int secs) {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(secs));
return System.Threading.Tasks.Task.Run(() => func());
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine(DateTime.Now);
Toolz.SetTimeout(() => {
Console.WriteLine(DateTime.Now);
return "";
}, 10);
}
}

I just ran into this in a .NET 4.0 app (no access to Task.Run, Task.Delay, etc.). If you will excuse the last line (which is the setTimeout part) it's fairly concise.
int sleepTime = 10000;
Action myAction = () => {
// my awesome cross-thread update code
this.BackColor = Color.Red;
};
new System.Threading.Thread(() => { System.Threading.Thread.Sleep(sleepTime); if (InvokeRequired) myAction(); else myAction(); }).Start();

Related

Task doesn't stop [duplicate]

In a thread, I create some System.Threading.Task and start each task.
When I do a .Abort() to kill the thread, the tasks are not aborted.
How can I transmit the .Abort() to my tasks ?
You can't. Tasks use background threads from the thread pool. Also canceling threads using the Abort method is not recommended. You may take a look at the following blog post which explains a proper way of canceling tasks using cancellation tokens. Here's an example:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Like this post suggests, this can be done in the following way:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Although it works, it's not recommended to use such approach. If you can control the code that executes in task, you'd better go with proper handling of cancellation.
Aborting a Task is easily possible if you capture the thread in which the task is running in. Here is an example code to demonstrate this:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
I used Task.Run() to show the most common use-case for this - using the comfort of Tasks with old single-threaded code, which does not use the CancellationTokenSource class to determine if it should be canceled or not.
This sort of thing is one of the logistical reasons why Abort is deprecated. First and foremost, do not use Thread.Abort() to cancel or stop a thread if at all possible. Abort() should only be used to forcefully kill a thread that is not responding to more peaceful requests to stop in a timely fashion.
That being said, you need to provide a shared cancellation indicator that one thread sets and waits while the other thread periodically checks and gracefully exits. .NET 4 includes a structure designed specifically for this purpose, the CancellationToken.
I use a mixed approach to cancel a task.
Firstly, I'm trying to Cancel it politely with using the Cancellation.
If it's still running (e.g. due to a developer's mistake), then misbehave and kill it using an old-school Abort method.
Checkout an example below:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
To answer Prerak K's question about how to use CancellationTokens when not using an anonymous method in Task.Factory.StartNew(), you pass the CancellationToken as a parameter into the method you're starting with StartNew(), as shown in the MSDN example here.
e.g.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
You should not try to do this directly. Design your tasks to work with a CancellationToken, and cancel them this way.
In addition, I would recommend changing your main thread to function via a CancellationToken as well. Calling Thread.Abort() is a bad idea - it can lead to various problems that are very difficult to diagnose. Instead, that thread can use the same Cancellation that your tasks use - and the same CancellationTokenSource can be used to trigger the cancellation of all of your tasks and your main thread.
This will lead to a far simpler, and safer, design.
Tasks have first class support for cancellation via cancellation tokens. Create your tasks with cancellation tokens, and cancel the tasks via these explicitly.
You can use a CancellationToken to control whether the task gets cancelled. Are you talking about aborting it before it's started ("nevermind, I already did this"), or actually interrupting it in middle? If the former, the CancellationToken can be helpful; if the latter, you will probably need to implement your own "bail out" mechanism and check at appropriate points in the task execution whether you should fail fast (you can still use the CancellationToken to help you, but it's a little more manual).
MSDN has an article about cancelling Tasks:
http://msdn.microsoft.com/en-us/library/dd997396.aspx
Task are being executed on the ThreadPool (at least, if you are using the default factory), so aborting the thread cannot affect the tasks. For aborting tasks, see Task Cancellation on msdn.
I tried CancellationTokenSource but i can't do this. And i did do this with my own way. And it works.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
And another class of the calling the method:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
You can abort a task like a thread if you can cause the task to be created on its own thread and call Abort on its Thread object. By default, a task runs on a thread pool thread or the calling thread - neither of which you typically want to abort.
To ensure the task gets its own thread, create a custom scheduler derived from TaskScheduler. In your implementation of QueueTask, create a new thread and use it to execute the task. Later, you can abort the thread, which will cause the task to complete in a faulted state with a ThreadAbortException.
Use this task scheduler:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Start your task like this:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Later, you can abort with:
scheduler.TaskThread.Abort();
Note that the caveat about aborting a thread still applies:
The Thread.Abort method should be used with caution. Particularly when you call it to abort a thread other than the current thread, you do not know what code has executed or failed to execute when the ThreadAbortException is thrown, nor can you be certain of the state of your application or any application and user state that it is responsible for preserving. For example, calling Thread.Abort may prevent static constructors from executing or prevent the release of unmanaged resources.
You can use this class..:
It works for all typs of returned Values..
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CarNUChargeTester
{
public class TimeOutTaskRunner<T>
{
private Func<T> func;
private int sec;
private T result;
public TimeOutTaskRunner(Func<T> func, int sec)
{
this.func = func;
this.sec = sec;
}
public bool run()
{
var scheduler = new SingleThreadTaskScheduler();
Task<T> task = Task<T>.Factory.StartNew(func, (new CancellationTokenSource()).Token, TaskCreationOptions.LongRunning, scheduler);
if (!task.Wait(TimeSpan.FromSeconds(sec)))
{
scheduler.TaskThread.Abort();
return false;
}
result = task.Result;
return true;
}
public T getResult() { return result; }
}
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException();
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException();
}
}
To use it you can write:
TimeOutTaskRunner<string> tr = new TimeOutTaskRunner<string>(f, 10); // 10 sec to run f
if (!tr.run())
errorMsg("TimeOut"); !! My func
tr.getResult() // get the results if it done without timeout..

Task unable to timeout

I have a implemented a simple task using TPL. It waits for 10 seconds to execute and returns true/false.
var checkCFOPTask = Task.Run(() => CheckCFOPExists());
checkCFOPTask.Wait(TimeSpan.FromSeconds(10));
if (checkCFOPTask.Result)
{
}
else
{
}
The problem is my code is stuck within the if statement.
if (checkCFOPTask.Result)
Each time I pause the debugger it still keeps waiting on the above line of code. This happened for the first time. Ideally it should return true/false within 10 seconds.
Below are the function definitions-
CheckCFOExists: Executed by the task.
private bool CheckCFOPExists()
{
bool found = false;
try
{
while (!found)
{
try
{
if (ieDriver.FindElement(By.Id("popup_message")).Text == "Não existem itens para realizar o rateio.")
{
ResetInvoiceSearchScreen();
break;
}
}
catch (Exception ex)
{
}
try
{
if (arrCFOPList.Contains(ieDriver.FindElement(By.Id("vendorNF.cfopOperCode")).GetAttribute("value")))
{
found = true;
}
}
catch (Exception ex)
{
}
}
}
catch (Exception ex)
{
}
return found;
}
ResetInvoiceSearchScreen: Executed within the above function
private void ResetInvoiceSearchScreen()
{
try
{
ieDriver.FindElement(By.Id("popup_ok")).Click();
ieDriver.FindElement(By.Id("ltmCnpjCpf")).Clear();
ieDriver.FindElement(By.Id("notaFiscalNbr")).Clear();
ieDriver.FindElement(By.Id("inbNotaFiscalId")).Clear();
ieDriver.FindElement(By.Id("seriesFrmCd")).Clear();
}
catch (Exception ex)
{
}
}
Is there something else that is needed ensure the function times out correctly? Please let me know if I can provide some more details.
Edit
I see the below message for checkCFOPTask.Result in the immediate window of Visual Studio-
Id = Cannot evaluate expression because the code of the current method is optimized., Status = Cannot evaluate expression because the code of the current method is optimized., Method = Cannot evaluate expression because the code of the current method is optimized., Result = Cannot evaluate expression because the code of the current method is optimized.
It looks like you will need to add time-out support to the method that you're calling - because if it doesn't find the thing it's looking for it will loop forever.
The easiest way to do this is to pass a CancellationToken to the method. You should also factor out the testing code into a separate method that returns bool.
Also note that you have a busy loop, which is generally not a good idea when polling! It's better to introduce a small sleep when polling if the thing you're polling for isn't available. (Note: Polling in general is not a good approach if you have a better way of checking for something, but it doesn't look like there's anything else you can use here, so polling will have to do.)
You can write your method like so (I've omitted the code that polls for the thing you're looking for in order to concentrate on the other logic):
private bool CheckCFOPExists(CancellationToken cancel)
{
TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);
while (true)
{
if (tryToFindTheThing()) // Blocking call.
return true;
if (cancel.WaitHandle.WaitOne(retryDelay))
return false;
}
}
bool tryToFindTheThing()
{
return false; // Your implementation goes here.
}
Then to call this and have a 10-second timeout you would do something like this (compilable console app):
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static void Main()
{
var test = new Program();
// Create a cancellation token source that cancels itself after 10 seconds:
var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));
// Create and run the task:
var sw = Stopwatch.StartNew();
var checkCFOPTask = Task.Run(() => test.CheckCFOPExists(cancellation.Token));
Console.WriteLine("Waiting for task to finish.");
Console.WriteLine($"Task returned: {checkCFOPTask.Result} after {sw.ElapsedMilliseconds} ms");
}
private bool CheckCFOPExists(CancellationToken cancel)
{
TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);
while (true)
{
if (tryToFindTheThing()) // Blocking call.
return true;
if (cancel.WaitHandle.WaitOne(retryDelay))
return false;
}
}
bool tryToFindTheThing()
{
return false; // Your implementation goes here.
}
}
}
Before using the Result you need to check if your task actually is completed with Task.IsCompleted.
if (checkCFOPTask.IsCompleted && checkCFOPTask.Result)

Where to handle exceptions thrown by Task

I am executing some polling IO loops in separate Tasks. Those loops may encounter exceptions. If one encounters an exception, I want to alert the caller so that it can:
log it
kill all IO threads
reset the connection
restart IO threads
The UI must remain responsive. What is the preferred method of handling this scenario? I've include an illustrative program below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaskExceptionCatching
{
class Program
{
static void Main(string[] args)
{
startLoops();
System.Console.WriteLine("Type 'Exit' when you're ready to stop.");
while (System.Console.ReadLine() != "Exit")
{
System.Console.WriteLine("Seriously, just type 'Exit' when you're ready to stop.");
}
}
static private void startLoops()
{
System.Console.WriteLine("Starting fizzLoop.");
var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));
System.Console.WriteLine("Starting buzzLoop.");
var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));
}
static private void fizzLoop()
{
while (true)
{
//simulate something error prone, like some risky IO
System.Threading.Thread.Sleep(200);
bool isErr = (new Random().Next(1, 100) == 10);
if (isErr)
throw new Exception("Fizz got an exception.");
}
}
static private void buzzLoop()
{
while (true)
{
//simulate something error prone, like some risky IO
System.Threading.Thread.Sleep(200);
bool isErr = (new Random().Next(1, 100) == 10);
if (isErr)
throw new Exception("Buzz got an exception.");
}
}
}
}
This might be one of the rare cases when an async void method could be convenient:
static async void StartAndMonitorAsync(Func<Task> taskFunc)
{
while (true)
{
var task = taskFunc();
try
{
await task;
// process the result if needed
return;
}
catch (Exception ex)
{
// log the error
System.Console.WriteLine("Error: {0}, restarting...", ex.Message);
}
// do other stuff before restarting (if any)
}
}
static private void startLoops()
{
System.Console.WriteLine("Starting fizzLoop.");
StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(fizzLoop)));
System.Console.WriteLine("Starting buzzLoop.");
StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(buzzLoop)));
}
If you can't use async/await, a similar logic can be implemented using Task.ContinueWith.
If startLoops can be called multiple times (while tasks are already "in-flight"), you'd need to add cancellation logic to StartAndMonitorAsync and the tasks it sarts, using CancelltionToken (more details in "A pattern for self-cancelling and restarting task").
From MSDN
"The Task infrastructure wraps them in an AggregateException instance. The AggregateException has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. Even if only one exception is thrown, it is still wrapped in an AggregateException."
For more information you can check this
try
{
System.Console.WriteLine("Starting fizzLoop.");
var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));
System.Console.WriteLine("Starting buzzLoop.");
var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));
}
catch (AggregateException ae)
{
// Assume we know what's going on with this particular exception.
// Rethrow anything else. AggregateException.Handle provides
// another way to express this. See later example.
foreach (var e in ae.InnerExceptions)
{
if (e is MyCustomException)
{
Console.WriteLine(e.Message);
}
else
{
throw;
}
}
}

How do I abort/cancel TPL Tasks?

In a thread, I create some System.Threading.Task and start each task.
When I do a .Abort() to kill the thread, the tasks are not aborted.
How can I transmit the .Abort() to my tasks ?
You can't. Tasks use background threads from the thread pool. Also canceling threads using the Abort method is not recommended. You may take a look at the following blog post which explains a proper way of canceling tasks using cancellation tokens. Here's an example:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Like this post suggests, this can be done in the following way:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Although it works, it's not recommended to use such approach. If you can control the code that executes in task, you'd better go with proper handling of cancellation.
Aborting a Task is easily possible if you capture the thread in which the task is running in. Here is an example code to demonstrate this:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
I used Task.Run() to show the most common use-case for this - using the comfort of Tasks with old single-threaded code, which does not use the CancellationTokenSource class to determine if it should be canceled or not.
This sort of thing is one of the logistical reasons why Abort is deprecated. First and foremost, do not use Thread.Abort() to cancel or stop a thread if at all possible. Abort() should only be used to forcefully kill a thread that is not responding to more peaceful requests to stop in a timely fashion.
That being said, you need to provide a shared cancellation indicator that one thread sets and waits while the other thread periodically checks and gracefully exits. .NET 4 includes a structure designed specifically for this purpose, the CancellationToken.
I use a mixed approach to cancel a task.
Firstly, I'm trying to Cancel it politely with using the Cancellation.
If it's still running (e.g. due to a developer's mistake), then misbehave and kill it using an old-school Abort method.
Checkout an example below:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
To answer Prerak K's question about how to use CancellationTokens when not using an anonymous method in Task.Factory.StartNew(), you pass the CancellationToken as a parameter into the method you're starting with StartNew(), as shown in the MSDN example here.
e.g.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
You should not try to do this directly. Design your tasks to work with a CancellationToken, and cancel them this way.
In addition, I would recommend changing your main thread to function via a CancellationToken as well. Calling Thread.Abort() is a bad idea - it can lead to various problems that are very difficult to diagnose. Instead, that thread can use the same Cancellation that your tasks use - and the same CancellationTokenSource can be used to trigger the cancellation of all of your tasks and your main thread.
This will lead to a far simpler, and safer, design.
Tasks have first class support for cancellation via cancellation tokens. Create your tasks with cancellation tokens, and cancel the tasks via these explicitly.
You can use a CancellationToken to control whether the task gets cancelled. Are you talking about aborting it before it's started ("nevermind, I already did this"), or actually interrupting it in middle? If the former, the CancellationToken can be helpful; if the latter, you will probably need to implement your own "bail out" mechanism and check at appropriate points in the task execution whether you should fail fast (you can still use the CancellationToken to help you, but it's a little more manual).
MSDN has an article about cancelling Tasks:
http://msdn.microsoft.com/en-us/library/dd997396.aspx
Task are being executed on the ThreadPool (at least, if you are using the default factory), so aborting the thread cannot affect the tasks. For aborting tasks, see Task Cancellation on msdn.
I tried CancellationTokenSource but i can't do this. And i did do this with my own way. And it works.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
And another class of the calling the method:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
You can abort a task like a thread if you can cause the task to be created on its own thread and call Abort on its Thread object. By default, a task runs on a thread pool thread or the calling thread - neither of which you typically want to abort.
To ensure the task gets its own thread, create a custom scheduler derived from TaskScheduler. In your implementation of QueueTask, create a new thread and use it to execute the task. Later, you can abort the thread, which will cause the task to complete in a faulted state with a ThreadAbortException.
Use this task scheduler:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Start your task like this:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Later, you can abort with:
scheduler.TaskThread.Abort();
Note that the caveat about aborting a thread still applies:
The Thread.Abort method should be used with caution. Particularly when you call it to abort a thread other than the current thread, you do not know what code has executed or failed to execute when the ThreadAbortException is thrown, nor can you be certain of the state of your application or any application and user state that it is responsible for preserving. For example, calling Thread.Abort may prevent static constructors from executing or prevent the release of unmanaged resources.
You can use this class..:
It works for all typs of returned Values..
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CarNUChargeTester
{
public class TimeOutTaskRunner<T>
{
private Func<T> func;
private int sec;
private T result;
public TimeOutTaskRunner(Func<T> func, int sec)
{
this.func = func;
this.sec = sec;
}
public bool run()
{
var scheduler = new SingleThreadTaskScheduler();
Task<T> task = Task<T>.Factory.StartNew(func, (new CancellationTokenSource()).Token, TaskCreationOptions.LongRunning, scheduler);
if (!task.Wait(TimeSpan.FromSeconds(sec)))
{
scheduler.TaskThread.Abort();
return false;
}
result = task.Result;
return true;
}
public T getResult() { return result; }
}
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException();
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException();
}
}
To use it you can write:
TimeOutTaskRunner<string> tr = new TimeOutTaskRunner<string>(f, 10); // 10 sec to run f
if (!tr.run())
errorMsg("TimeOut"); !! My func
tr.getResult() // get the results if it done without timeout..

How to catch exceptions from a ThreadPool.QueueUserWorkItem?

I have the following code that throws an exception:
ThreadPool.QueueUserWorkItem(state => action());
When the action throws an exception, my program crashes. What is the best practice for handling this situation?
Related: Exceptions on .Net ThreadPool Threads
You can add try/catch like this:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
If you have access to action's source code, insert a try/catch block in that method; otherwise, create a new tryAction method which wraps the call to action in a try/catch block.
If you're using .Net 4.0, it might be worth investigating the Task class because it can take care of this for you.
The equivalent of your original code, but using Tasks, looks like
Task.Factory.StartNew(state => action(), state);
To deal with exceptions you can add a continuation to the Task returned by StartNew. It might look like this:
var task = Task.Factory.StartNew(state => action(), state);
task.ContinueWith(t =>
{
var exception = t.Exception.InnerException;
// handle the exception here
// (note that we access InnerException, because tasks always wrap
// exceptions in an AggregateException)
},
TaskContinuationOptions.OnlyOnFaulted);
On the other thread, (in the method you are "queueing" up, add a try catch clause... .Then in the catch, place the caught exception into a shared Exception variable (visible to the main thread).
Then in your main thread, when all queued items have finished (use a wait handle array for this) Check if some thread populated that shared exception with an exception... If it did, rethrow it or handle it as appropriate...
here's some sample code from a recent project I used this for...
HasException is shared boolean...
private void CompleteAndQueuePayLoads(
IEnumerable<UsagePayload> payLoads, string processId)
{
List<WaitHandle> waitHndls = new List<WaitHandle>();
int defaultMaxwrkrThreads, defaultmaxIOThreads;
ThreadPool.GetMaxThreads(out defaultMaxwrkrThreads,
out defaultmaxIOThreads);
ThreadPool.SetMaxThreads(
MDMImportConfig.MAXCONCURRENTIEEUSAGEREQUESTS,
defaultmaxIOThreads);
int qryNo = 0;
foreach (UsagePayload uPL in payLoads)
{
ManualResetEvent txEvnt = new ManualResetEvent(false);
UsagePayload uPL1 = uPL;
int qryNo1 = ++qryNo;
ThreadPool.QueueUserWorkItem(
delegate
{
try
{
Thread.CurrentThread.Name = processId +
"." + qryNo1;
if (!HasException && !uPL1.IsComplete)
IEEDAL.GetPayloadReadings(uPL1,
processId, qryNo1);
if (!HasException)
UsageCache.PersistPayload(uPL1);
if (!HasException)
SavePayLoadToProcessQueueFolder(
uPL1, processId, qryNo1);
}
catch (MeterUsageImportException iX)
{
log.Write(log.Level.Error,
"Delegate failed " iX.Message, iX);
lock (locker)
{
HasException = true;
X = iX;
foreach (ManualResetEvent
txEvt in waitHndls)
txEvt.Set();
}
}
finally { lock(locker) txEvnt.Set(); }
});
waitHndls.Add(txEvnt);
}
util.WaitAll(waitHndls.ToArray());
ThreadPool.SetMaxThreads(defaultMaxwrkrThreads,
defaultmaxIOThreads);
lock (locker) if (X != null) throw X;
}
What I usually do is to create a big try ... catch block inside the action() method
then store the exception as a private variable then handle it inside the main thread
Simple Code:
public class Test
{
private AutoResetEvent _eventWaitThread = new AutoResetEvent(false);
private void Job()
{
Action act = () =>
{
try
{
// do work...
}
finally
{
_eventWaitThread.Set();
}
};
ThreadPool.QueueUserWorkItem(x => act());
_eventWaitThread.WaitOne(10 * 1000 * 60);
}
}

Categories