Process.WaitForExit() asynchronously - c#

I want to wait for a process to finish, but Process.WaitForExit() hangs my GUI. Is there an event-based way, or do I need to spawn a thread to block until exit, then delegate the event myself?

As of .NET 4.0/C# 5, it's nicer to represent this using the async pattern.
/// <summary>
/// Waits asynchronously for the process to exit.
/// </summary>
/// <param name="process">The process to wait for cancellation.</param>
/// <param name="cancellationToken">A cancellation token. If invoked, the task will return
/// immediately as canceled.</param>
/// <returns>A Task representing waiting for the process to end.</returns>
public static Task WaitForExitAsync(this Process process,
CancellationToken cancellationToken = default(CancellationToken))
{
if (process.HasExited) return Task.CompletedTask;
var tcs = new TaskCompletionSource<object>();
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => tcs.TrySetResult(null);
if(cancellationToken != default(CancellationToken))
cancellationToken.Register(() => tcs.SetCanceled());
return process.HasExited ? Task.CompletedTask : tcs.Task;
}
Usage:
public async void Test()
{
var process = new Process("processName");
process.Start();
await process.WaitForExitAsync();
//Do some fun stuff here...
}

process.EnableRaisingEvents = true;
process.Exited += [EventHandler]

UPDATE: .NET 5 now includes Process.WaitForExitAsync() natively, you can find the implementation here. It's very similar to the below extension method.
Previous Answer:
Here's an extension method that's slightly cleaner, because it cleans up the cancellation token registration and Exited event. It also handles the race condition edge cases, where the process could end after it started, but before the Exited event was attached. It uses the new local functions syntax in C# 7. The return value is the process return code.
public static class ProcessExtensions
{
public static async Task<int> WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
void Process_Exited(object sender, EventArgs e)
{
tcs.TrySetResult(process.ExitCode);
}
try
{
process.EnableRaisingEvents = true;
}
catch (InvalidOperationException) when (process.HasExited)
{
// This is expected when trying to enable events after the process has already exited.
// Simply ignore this case.
// Allow the exception to bubble in all other cases.
}
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
{
process.Exited += Process_Exited;
try
{
if (process.HasExited)
{
tcs.TrySetResult(process.ExitCode);
}
return await tcs.Task.ConfigureAwait(false);
}
finally
{
process.Exited -= Process_Exited;
}
}
}
}

If you choose #MgSam answer, be aware, if you pass through WaitForExitAsync some CancellationToken, that will be automatically canceled after the specified delay, you can get an InvalidOperationException. To fix that, you need to change
cancellationToken.Register(tcs.SetCanceled);
to
cancellationToken.Register( () => { tcs.TrySetCanceled(); } );
P.S.: don't forget to dispose your CancellationTokenSource in time.

As of .NET5 there is now a WaitForExitAsync provided by the Process class.
await process.WaitForExitAsync( token );

According to this link the WaitForExit() method is used to make the current thread wait until the associated process terminates. However, the Process does have an Exited event that you can hook into.

Ryan solution works good on windows. On OSX strange things happened it could be a Deadlock at tcs.TrySetResult()! There are 2 solutions:
First one:
Wrap tcs.TrySetResult() to a Task.Run():
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<bool>();
void Process_Exited(object sender, EventArgs e)
{
Task.Run(() => tcs.TrySetResult(true));
}
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
try
{
if (process.HasExited)
{
return;
}
using (cancellationToken.Register(() => Task.Run(() => tcs.TrySetCanceled())))
{
await tcs.Task;
}
}
finally
{
process.Exited -= Process_Exited;
}
}
Conversation about this and more details:
Calling TaskCompletionSource.SetResult in a non blocking manner
Second one:
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken)
{
while (!process.HasExited)
{
await Task.Delay(100, cancellationToken);
}
}
You can increase the Polling interval from 100 ms to more depending your application.

Fast and simple solution:
async Task RunProcessWait(string Path)
{
Process MyProcess = Process.Start(Path);
while (!MyProcess.HasExited)
await Task.Delay(100);
}
await RunProcessWait("C:\...")

Use System.Diagnostics.Process.Exited

Related

Why doesn't console application exit after last statement when running in Visual Studio 2019 debugger?

I am looking for an async friendly way to wait for ctrl+c to exit a C# console application. The code below works if I run the compiled binary directly. However, if I run it in the debugger, input ctrl+c, and step through I will hit the closing brace of the program but it does not exit. If I comment out the await, the application finishes normally so I don't think it's a VS settings issue.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Console.WriteLine("Hello World");
await UntilCancelled().ConfigureAwait(true);
Console.WriteLine("Goodbye Cruel World");
}
/// <summary>
/// Waits until Ctrl+c or Ctrl+Break is entered into the console
/// </summary>
/// <param name="cancellationToken">A passed in cancellation token can also cause the await to complete</param>
/// <returns>True when cancelled</returns>
public static Task UntilCancelled(CancellationToken cancellationToken = default)
{
var cts = new CancellationTokenSource();
//link tokens if caller wants to have ability to cancel for other conditions
if (default != cancellationToken)
cts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken);
//Attach cancellation to event for exiting the console
Console.CancelKeyPress += (sender, cpe) => cts.Cancel();
var tcs = new TaskCompletionSource<bool>();
cts.Token.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
It is very interesting question.
And in Visual Studio when you press pause debug button you can open and see the list of active tasks and/or threads.
I paused the execution.
And double clicked the thread
Selected "Download Sources and Continue Debugging"
And came to this code:
According to the comments and the method name I would come to conclusion that it is related somehow to debug mode and the fact that the execution finished unsuccessfully. And indeed, when I launched it as Ctrl+F5, i.e. without debugger attached it worked.
And also you can see there return code is not a zero, which also says that execution was not successfull.
So, it may be related to the debugger and that you cancel the task but don't catch it.
Upd1:
Going further I found that the event registration finally calls SetConsoleCtrlHandler
https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler
And according to that
Return value
If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used.
Returned value is the Cancel property of the Cancel...Args parameter.
And they also say
By default, the Cancel property is false, which causes program execution to terminate when the event handler exits. Changing its property to true specifies that the application should continue to execute.
And for me it looks confusing, but I guess you need to set Cancel to true which indicates that the application handled the cancel event.
Upd2:
I changed the Console event with winapi SetConsoleCtrlHandler and the same behavior happens. And if you change false to true in the ConsoleCtrlDelegate, the application will exit successfully. So I think in the debug mode there might be another event handler which blocks execution, but I'm not sure.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Console.WriteLine("Hello World");
await UntilCancelled();
Console.WriteLine("Goodbye Cruel World");
}
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine,
bool Add);
// Delegate type to be used as the Handler Routine for SCCH
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
/// <summary>
/// Waits until Ctrl+c or Ctrl+Break is entered into the console
/// </summary>
/// <param name="cancellationToken">A passed in cancellation token can also cause the await to complete</param>
/// <returns>True when cancelled</returns>
public static Task UntilCancelled()
{
var cts = new CancellationTokenSource();
ConsoleCtrlDelegate handler = (cpe) =>
{
cts.Cancel();
return false;
};
SetConsoleCtrlHandler(handler, true);
var tcs = new TaskCompletionSource<bool>();
cts.Token.Register(s => tcs.SetResult(true), null);
return tcs.Task;
}
}
Try this:
public static Task UntilCancelled(CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(() => tcs.TrySetResult(true));
Console.CancelKeyPress += (sender, cpe) =>
{
// this will prevent windows from interfering with the shutdown process
cpe.Cancel = true;
tcs.TrySetResult(true);
};
return tcs.Task;
}
I tested it with this:
var cts = new CancellationTokenSource();
Console.WriteLine("Started");
Task.Run(async () =>
{
await Task.Delay(5000);
cts.Cancel(false);
}).GetAwaiter();
await Test.UntilCancelled(cts.Token);
Console.WriteLine("Canceled");

Updating an asyncronous polling library to modern async paradigm

I have this (working) bare-bones implementation of an asynchronous polling callback loop:
public void Start(ICallback callback)
{
if (Callback != null)
Stop();
Console.WriteLine("STARTING");
Callback = callback;
cancellation = new CancellationTokenSource();
this.task = Task.Run(() => TaskLoop(), cancellation.Token);
Console.WriteLine("STARTED");
}
public void Stop()
{
if (Callback == null)
{
Console.WriteLine("ALREADY stopped");
return;
}
Console.WriteLine("STOPPING");
cancellation.Cancel();
try
{
task.Wait();
}
catch (Exception e)
{
Console.WriteLine($"{e.Message}");
}
finally
{
cancellation.Dispose();
cancellation = null;
Callback = null;
task = null;
Console.WriteLine("STOPPED");
}
}
private void TaskLoop()
{
int i = 0;
while (!cancellation.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine("Starting iteration... {0}", i);
Task.Run(() =>
{
//just for testing
Callback.SendMessage($"Iteration {i} at {System.DateTime.Now}");
}).Wait();
Console.WriteLine("...Ending iteration {0}", i++);
}
Console.WriteLine("CANCELLED");
}
It's actually called from unmanaged C++ via COM, so this is a library project (and the callback is a COM-marshalled object) hence wanting to test the design first.
I'm switching to using the async paradigm and wonder if it should be as simple as sprinkling some async dust on my method declarations and swapping Wait() calls for await? Obviously Thread.Sleep would be changed for Task.Delay.
I am fairly sure COM will dedicate a thread to this object for marshaling purposes and unmanaged C++ doesn't know about the .Net async model, so are there any gotchas/pitfalls to watch out for?
This is an updated version I'm testing but, like Resource management, multithreading is an area your code can seem to work perfectly but is actually quite badly broken so I'd appreciate thoughts:
public void Start(ICallback callback)
{
if (Callback != null)
Stop();
Console.WriteLine("STARTING");
Callback = callback;
cancellation = new CancellationTokenSource();
this.task = TaskLoopAsync();
Console.WriteLine("STARTED");
}
public async void Stop()
{
if (Callback == null)
{
Console.WriteLine("ALREADY stopped");
return;
}
Console.WriteLine("STOPPING");
cancellation.Cancel();
try
{
await task;
}
catch (Exception e)
{
Console.WriteLine($"{e.Message}");
}
finally
{
cancellation.Dispose();
cancellation = null;
Callback = null;
task = null;
Console.WriteLine("STOPPED");
}
}
private async void TaskLoopAsync()
{
int i = 0;
while (!cancellation.IsCancellationRequested)
{
await Task.Delay(1000);
Console.WriteLine("Starting iteration... {0}", i);
Callback.SendMessage($"Iteration {i} at {System.DateTime.Now}");
Console.WriteLine("...Ending iteration {0}", i++);
}
Console.WriteLine("CANCELLED");
}
unmanaged C++ doesn't know about the .Net async model, so are there any gotchas/pitfulls to watch out for?
Just that one. It's fine to apply async/await to your internal code (e.g., TaskLoop), but you can't let it extend out to the COM boundary. So Start and Stop cannot be made async.
Switching to async introduced a bug in your code. The problem is with the async void Stop method. It is called from inside the Start method, and since there is no way to await an async void method, both methods are executing concurrently for a while. So it is a matter of luck which of the two commands below will be executed first:
this.task = TaskLoopAsync(); // in Start method
task = null; // in Stop method
Other points of interest:
The CancellationTokenSource is not used the way it is intended. This class is more than a glorified volatile bool. It also allows to cancel asynchronous operations an any time by registering callbacks. For example you can cancel the asynchronous Task.Delay immediately by passing the token through its second optional argument:
await Task.Delay(1000, cancellation.Token);
Then you must be prepared to handle the OperationCanceledException that will be thrown when the token is canceled. This is the standard way of communicating cancellation in .NET, by throwing this exception from the one end and capturing it from the other.
You can achieve more consistent intervals between calling the Callback.SendMessage method, by creating the Task.Delay task before calling the method, and awaiting it afterwards.

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..

How Do I Create a Looping Service inside an C# Async/Await application?

I have written a class with a method that runs as a long-running Task in the thread pool. The method is a monitoring service to periodically make a REST request to check on the status of another system. It's just a while() loop with a try()catch() inside so that it can handle its own exceptions and and gracefully continuing if something unexpected happens.
Here's an example:
public void LaunchMonitorThread()
{
Task.Run(() =>
{
while (true)
{
try
{
//Check system status
Thread.Sleep(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
});
}
It works fine, but I want to know if there's another pattern I could use that would allow the Monitor method to run as regular part of a standard Async/Await application, instead of launching it with Task.Run() -- basically I'm trying to avoid fire-and-forget pattern.
So I tried refactoring the code to this:
public async Task LaunchMonitorThread()
{
while (true)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
But when I try to call the method in another async method, I get the fun compiler warning:
"Because this call is not awaited, execution of the current method continues before the call is completed."
Now I think this is correct and what I want. But I have doubts because I'm new to async/await. Is this code going to run the way I expect or is it going to DEADLOCK or do something else fatal?
What you are really looking for is the use of a Timer. Use the one in the System.Threading namespace. There is no need to use Task or any other variation thereof (for the code sample you have shown).
private System.Threading.Timer timer;
void StartTimer()
{
timer = new System.Threading.Timer(TimerExecution, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
}
void TimerExecution(object state)
{
try
{
//Check system status
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
From the documentation
Provides a mechanism for executing a method on a thread pool thread at specified intervals
You could also use System.Timers.Timer but you might not need it. For a comparison between the 2 Timers see also System.Timers.Timer vs System.Threading.Timer.
If you need fire-and-forget operation, it is fine. I'd suggest to improve it with CancellationToken
public async Task LaunchMonitorThread(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000, token);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
besides that, you can use it like
var cancellationToken = new CancellationToken();
var monitorTask = LaunchMonitorThread(cancellationToken);
and save task and/or cancellationToken to interrupt monitor wherever you want
The method Task.Run that you use to fire is perfect to start long-running async functions from a non-async method.
You are right: the forget part is not correct. If for instance your process is going to close, it would be neater if you kindly asked the started thread to finish its task.
The proper way to do this would be to use a CancellationTokenSource. If you order the CancellationTokenSource to Cancel, then all procedures that were started using Tokens from this CancellationTokenSource will stop neatly within reasonable time.
So let's create a class LongRunningTask, that will create a long running Task upon construction and Cancel this task using the CancellationTokenSource upon Dispose().
As both the CancellationTokenSource as the Task implement IDisposable the neat way would be to Dispose these two when the LongRunningTask object is disposed
class LongRunningTask : IDisposable
{
public LongRunningTask(Action<CancellationToken> action)
{ // Starts a Task that will perform the action
this.cancellationTokenSource = new CancellationTokenSource();
this.longRunningTask = Task.Run( () => action (this.cancellationTokenSource.Token));
}
private readonly CancellationTokenSource cancellationTokenSource;
private readonly Task longRunningTask;
private bool isDisposed = false;
public async Task CancelAsync()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
await this.longRunningTask;
}
// for completeness a non-async version:
public void Cancel()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
this.longRunningTask.Wait;
}
}
Add a standard Dispose Pattern
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{ // cancel the task, and wait until task completed:
this.Cancel();
this.IsDisposed = true;
}
}
Usage:
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
The Action {...} has a CancellationToken as input parameter, your function should regularly check it
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
Instead of checking for Token, you could call token.ThrowIfCancellationRequested. This will throw an exception that you'll have to catch

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..

Categories