When to dispose of System.Threading.Task with child tasks? - c#

I have a task that launches several child tasks. (e.g., Task A creates B,C,D,E,F). I also create a System.Threading.Timer to poll a database every 10 seconds to check if the scheduled item was cancelled by request. If it does, it sets CancellationTokenSource so that the task knows to cancel. Each sub-task, in this case B,C,D,E,F, will cancel when appropriate (they are looping thru files and moving them around).
Since Task implements IDisposable, I want to know if it is a good idea to call Task.WaitAll again from the catch block, to wait for the cancellations to propogate. While the cancellation request will be processed, the sub-tasks may be in the middle of a loop and can't cancel until that completes
However, per MSDN:
Always call Dispose before you release your last reference to the Task. Otherwise, the resources it is using will not be freed until the garbage collector calls the Task object's Finalize method.
Should I call wait again on my task array in order to properly call Dispose() on each task in the array?
public class MyCancelObject
{
CancellationTokenSource Source { get;set;}
int DatabaseId { get;set;}
}
private void CheckTaskCancelled(object state)
{
MyCancelObject sourceToken = (MyCancelObject)state;
if (!sourceToken.CancelToken.IsCancellationRequested)
{
//Check database to see if cancelled -- if so, set to cancelled
sourceToken.CancelToken.Cancel();
}
}
private void SomeFunc()
{
Task.StartNew( () =>
{
MyCancelObject myCancelObject = new MyCancelObject(
databaseId,
new CancellationTokenSource());
System.Threading.Timer cancelTimer = new Timer(
new TimerCallback(CheckIfTaskCancelled),
myCancelObject,
10000,
10000);
Task[] someTasks = new Task[someNumberOfTasks];
for (int i = 0; i < someNumberOfTasks; i++)
someTasks[i] = Task.Factory.StartNew(
() =>
{
DoSomeWork(someObject, myCancelObject.CancelToken.Token);
},
TaskCreationOptions.AttachedToParent | TaskCreationOptions.LongRunning,
myCancelObject.CancelToken.Token);
try
{
Task.WaitAll(someTasks, cts);
}
catch (AggregateException)
{
//Do stuff to handle
}
catch (OperationCanceledException)
{
//Should I call Task.WaitAll(someTasks) again??
//I want to be able to dispose.
}
}
}

I feel like I have figured this out, but anyone that would like to add anything else useful is more than welcome.
I simply called Task.WaitAll() again from the catch block to wait for the other tasks to finish. After they have all finished, I have a finally block cleaning up all tasks in the array.
try
{
Task.WaitAll(someTaskArray, cancelToken)
}
catch (OperationCanceledException)
{
Task.WaitAll(someTaskArray);
}
finally
{
for (int i = 0; i < someTaskArray.Length; i++)
someTaskArray[i].Dispose();
}

Related

How do you run a variable number of concurrent parametrizable infinite loop type of threads in C#?

I am creating my first multithreading C#/.NET based app that will run on a Azure Service Fabric cluster. As the title says, I wish to run a variable number of concurrent parametrizable infinite-loop type of threads, that will utilize the RunAsync method.
Each child thread looks something like this:
public async Task childThreadCall(...argument list...)
{
while (true)
{
try
{
//long running code
//do something useful here
//sleep for an independently parameterizable period, then wake up and repeat
}
catch (Exception e)
{
//Exception Handling
}
}
}
There are a variable number of such child threads that are called in the RunAsync method. I want to do something like this:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
try
{
for (int i = 0; i < input.length; i++)
{
ThreadStart ts[i] = new ThreadStart(childThreadCall(...argument list...));
Thread tc[i] = new Thread(ts);
tc[i].Start();
}
}
catch (Exception e)
{
//Exception Handling
}
}
So basically each of the child threads run independently from the others, and keep doing so forever. Is it possible to do such a thing? Could someone point me in the right direction? Are there any pitfalls to be aware of?
The RunAsync method is called upon start of the service. So yes it can be used to do what you want. I suggest using Tasks, as they play nicely with the cancelation token. Here is a rough draft:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var tasks = new List<Task>();
try
{
for (int i = 0; i < input.length; i++)
{
tasks.Add(MyTask(cancellationToken, i);
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
//Exception Handling
}
}
public async Task MyTask(CancellationToken cancellationToken, int a)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
//long running code, if possible check for cancellation using the token
//do something useful here
await SomeUseFullTask(cancellationToken);
//sleep for an independently parameterizable period, then wake up and repeat
await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
}
catch (Exception e)
{
//Exception Handling
}
}
}
Regarding pitfalls, there is a nice list of things to think of in general when using Tasks.
Do mind that Tasks are best suited for I/O bound work. If you can post what exactly is done in the long running process please do, then I can maybe improve the answer to best suit your use case.
One important thing it to respect the cancellation token passed to the RunAsync method as it indicates the service is about to stop. It gives you the opportunity to gracefully stop your work. From the docs:
Make sure cancellationToken passed to RunAsync(CancellationToken) is honored and once it has been signaled, RunAsync(CancellationToken) exits gracefully as soon as possible. Please note that if RunAsync(CancellationToken) has finished its intended work, it does not need to wait for cancellationToken to be signaled and can return gracefully.
As you can see in my code I pass the CancellationToken to child methods so they can react on a possible cancellation. In your case there will be a cancellation because of the endless loop.

Recursively call a method with the same thread

I have the following method:
public async Task ScrapeObjects(int page = 1)
{
try
{
while (!isObjectSearchCompleted)
{
..do calls..
}
}
catch (HttpRequestException ex)
{
Thread.Sleep(TimeSpan.FromSeconds(60));
ScrapeObjects(page);
Log.Fatal(ex, ex.Message);
}
}
I call this long running method async and I don't wait for it to finish. Thing is that an exception my occur and in that case I want to handle it. But then I want to start from where I left and with the same thread. At the current state a new thread gets used when I recursively call the method after handling the exception. I would like to keep using the same thread. Is there a way to do so? Thank you!
You probably need to move the try/catch block inside the while loop, and add a counter with the errors occurred, to bail out in case of continuous faulted attempts.
public async Task ScrapeObjects()
{
int failedCount = 0;
int page = 1;
while (!isObjectSearchCompleted)
{
try
{
//..do calls..
}
catch (HttpRequestException ex)
{
failedCount++;
if (failedCount < 3)
{
Log.Info(ex, ex.Message);
await Task.Delay(TimeSpan.FromSeconds(60));
}
else
{
Log.Fatal(ex, ex.Message);
throw; // or return;
}
}
}
}
As a side note it is generally better to await Task.Delay instead of Thread.Sleep inside asynchronous methods, to avoid blocking a thread without a reason.
One simple question before you read the long answer below:
Why you need the same thread? Are you accessing thread static / contextual data?
If yes, there will be ways to solve that easily than limiting your tasks to run on the same thread.
How to limit tasks to run on a single thread
As long as you use async calls on the default synchronization context, and as soon as the code is resumed from an await, it is possible that the thread can change after an await. This is because the default context schedules tasks to the next available thread in the thread pool. Like in the below case, before can be different from after:
public async Task ScrapeObjects(int page = 1)
{
var before = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(1000);
var after = Thread.CurrentThread.ManagedThreadId;
}
The only reliable way to guarantee that your code could come back on the same thread is to schedule your async code onto a single threaded synchronization context:
class SingleThreadSynchronizationContext : SynchronizationContext
{
private readonly BlockingCollection<Action> _actions = new BlockingCollection<Action>();
private readonly Thread _theThread;
public SingleThreadSynchronizationContext()
{
_theThread = new Thread(DoWork);
_theThread.IsBackground = true;
_theThread.Start();
}
public override void Send(SendOrPostCallback d, object state)
{
// Send requires run the delegate immediately.
d(state);
}
public override void Post(SendOrPostCallback d, object state)
{
// Schedule the action by adding to blocking collection.
_actions.Add(() => d(state));
}
private void DoWork()
{
// Keep picking up actions to run from the collection.
while (!_actions.IsAddingCompleted)
{
try
{
var action = _actions.Take();
action();
}
catch (InvalidOperationException)
{
break;
}
}
}
}
And you need to schedule ScrapeObjects to the custom context:
SynchronizationContext.SetSynchronizationContext(new SingleThreadSynchronizationContext());
await Task.Factory.StartNew(
() => ScrapeObjects(),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
TaskScheduler.FromCurrentSynchronizationContext()
).Unwrap();
By doing that, all your async code shall be scheduled to the same context, and run by the thread on that context.
However
This is typically dangerous, as you suddenly lose the ability to use the thread pool. If you block the thread, the entire async operation is blocked, meaning you will have deadlocks.

Correct Way to do Task Synchronization?

Is what I'm doing below the correct/best way to accomplish this?
I have a window with a timer. Each time the timer ticks, I call the RunTask method shown below. Within RunTask, I call DoTheThing. DoTheThing may take a while to run, and may fail (it's a database update). I want to make sure that at any point in time, I only have one DoTheThing outstanding. I also want to make sure that I don't have a bunch of RunTask instances all queued and waiting for a lock to be released by the RunTask instance that is running DoTheThing.
public void RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
Task task = new Task(() => DoTheThing());
task.Start();
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
lock (this.runTaskLock)
{
this.isTaskRunning = false;
}
}
}
}
Because of the architecture of the program, I would rather put all of my thread synchronization within this method instead of enabling and disabling the timer.
By thinking about the problem slightly differently, it becomes a lot easier. Instead of firing a timer every x seconds, why not wait x seconds between invocations?
Now you can just run an async loop to do the scheduled work and save yourself a bunch of painful synchronization work.
async Task RunActionPeriodicallyAsync(Action action,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
action();
await Task.Delay(ts, token);
//or alternatively (see comment below)
//var delayTask = Task.Delay(ts, token);
//action();
//await delayTask;
}
}
Now, just call RunActionPeriodicallyAsync once, and calls to its action will never overlap.
RunActionPeriodicallyAsync(() => DoSomething(), TimeSpan.FromSeconds(10))
You could overload this to take an async "action"... actually a Func<Task>...
async Task RunActionPeriodicallyAsync(Func<CancellationToken, Task> actionAsync,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
await actionAsync(token);
await Task.Delay(ts, token);
//or alternatively (see comment below)
//await Task.WhenAll(actionAsync(token), Task.Delay(ts, token))
}
}
and use it:
RunActionPeriodicallyAsync(async cancTok => await DoSomethingAsync(cancTok),
TimeSpan.FromSeconds(10))
If you are worried about too much locking, you can do the following. You might miss a run if one task completes while the other is just at the check (marked), but you got rid of some locking and you will only need to lock when you set isTaskRunnung = true.
In Addition you need to mark your method as async so you can await the task.
public async Task RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
if (this.isTaskRunning)
{ // <-- ___MARK___
canRunTask = false;
}
else
{
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
await Task.Run(() => DoTheThing());
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
this.isTaskRunning = false;
}
}
}

Cancelling BlockingCollection.GetConsumingEnumerable() and processing what's left

I have one process generating work and a second process with a BlockingCollection<> that consumes that work. When I close my program, I need my consumer to stop consuming work, but I still need to quickly log the work that was pending but hadn't been consumed.
Right now, my consumer spawns a thread that has a foreach (<object> in BlockingCollection.GetConsumingEnumerable()) loop. When I stop my program, my producer calls Consumer.BlockingCollection.CompleteAdding(). What I find is that my consumer continues to process everything in the queue.
Googling the issues tells me that I need to use a CancellationToken. So I tried that out:
private void Process () { // This method runs in a separate thread
try {
foreach (*work* in BlockingCollection.GetConsumingEnumerable(CancellationToken)) {
// Consume
}
}
catch (OperationCancelledException) {
foreach (*work* in BlockingCollection.GetConsumingEnumerable()) {
// quickly log
}
}
}
My producer has:
private CancellationTokenSource StopFlag = new CancellationTokenSource ();
MyConsumer.CancellationToken = StopFlag.Token;
// Make the consumer spawn it's consuming thread...
StopFlag.Cancel ();
MyConsumer.BlockingCollection.CompleteAdding ();
When I try this, I get no indication that the OperationCancelledException ever happened.
This question tries to explain the use of a cancellation token, but it seems like it's not using it correctly. (Argument: if it works, then it's "correct enough".) And this question would appear to be an exact duplicate of my question but with no example. (Same here.)
So to reiterate: How do I properly use a CancellationToken on BlockingCollection.GetConsumingEnumerable() with the caveat that I need to process the remaining items in the queue after it gets cancelled using a different method?
(I think my problem is centered around the proper use of the CancellationToken. None of my testing indicates that the process is actually being cancelled. (StopFlag.IsCancellationRequested always equals false.))
When you pass in the CancellationToken to GetConsumingEnumerable it won't throw an exception of cancellation is requested, it'll just stop spitting out items. Rather than catching the exception just check the token:
foreach (var item in BlockingCollection.
GetConsumingEnumerable(CancellationToken))
{
//consume item
}
if (CancellationToken.IsCancellationRequested)
foreach (var item in BlockingCollection)
{
//log item
}
Also note that, if cancellation is requested, and it's possible that CompletedAdding hasn't been called then you should just iterate the collection, not call GetConsumingEnumerable. If you know that the producer will complete adding when the operation is cancelled then that's not a problem.
My problem was in how I was trying to cancel the operation. Instead of having my producer owning the CancellationTokenSource, I put it all in the consumer.
public class cProducer {
private cConsumer myConsumer = new cConsumer ();
public void onStart () {
myConsumer.OnStart ();
}
public void onStop () {
myConsumer.OnStop ();
}
public void OnOrderReceived (cOrder newOrder) {
myConsumer.orderQueue.Add (cOrder);
}
}
public class cConsumer {
private CancellationTokenSource stopFlag;
public BlockingCollection<cOrder> orderQueue = new BlockingCollection<cOrder> ();
private Task processingTask;
public void OnStart () {
stopFlag = new CancellationTokenSource ();
processingTask = Task.Factory.StartNew (() => Process ());
}
public void OnStop () {
stopFlag.Cancel ();
orderQueue.CompleteAdding ();
processingTask.Wait ();
}
private void Process () {
try {
foreach (cOrder newOrder in orderQueue.GetConsumingEnumerable (stopFlag.Token)) {
// process
}
}
catch (OperationCanceledException) {
foreach (cOrder cancelledOrder in orderQueue.GetConsumingEnumerable ()) {
// log it
}
}
}
}
I had the exact same problem. The BlockingCollection seemed to be deadlocked when I cancelled the procedure. The OperationCanceledException was not propagated to the calling method. I figured out that my Producer did not take the cancellation token in consideration and was therefore waiting for the queue to be consumed. All I had to do was to provide the cancellation token in the Add() method. To translate this into Jason's solution above, all I did was this:
public void OnOrderReceived (cOrder newOrder, CancellationToken cancellationToken)
{
myConsumer.orderQueue.Add (cOrder, cancellationToken);
}
The Process() method does not need any try-catch clause. However you need to throw if the process is cancelled:
private void Process ()
{
foreach (cOrder newOrder in orderQueue.GetConsumingEnumerable (stopFlag.Token))
{
// process
stopFlag.Token.ThrowIfCancellationRequested();
}
}

Cancel Long Running task [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..

Categories

Resources