I want to add ability to cancel long running external method (in code below is it LongOperation method from SomeExternalClass class), so I wrap this method into task and add custom task extension for handle timeout.
TimeoutException is successful throwed after specific period of time, but external method is not canceled. How I can cancel LongOperation?
class Program
{
private static CancellationTokenSource cancellationTokenSource;
static async Task Main(string[] args)
{
cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Token.ThrowIfCancellationRequested();
try
{
await SearchTask().WithTimeout(TimeSpan.FromSeconds(2));
}
catch (TimeoutException)
{
Console.WriteLine("Timeout");
cancellationTokenSource.Cancel();
}
Console.WriteLine("Program end");
Console.ReadKey();
}
private static Task SearchTask()
{
return Task.Run(() =>
{
Console.WriteLine("Start task");
SomeExternalClass.LongOperation();
Console.WriteLine("End task");
}, cancellationTokenSource.Token);
}
}
public static class SomeExternalClass
{
// this is simulation of long running 3rd party method
public static void LongOperation()
{
Console.WriteLine("Start LongOperation");
Thread.Sleep(10000);
Console.WriteLine("End LongOperation");
}
}
public static class TaskExtension
{
public static async Task WithTimeout(this Task task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
await task;
}
throw new TimeoutException();
}
}
The output is:
Start task
Start LongOperation
Timeout
Program end
End LongOperation
End task
Related
We have below class members for timer:
private Timer _activityTimer;
Instantiating this timer variable in one method:
_activityTimer =
new Timer(async (timerState) => await UpdateActivityAsync(_ipAddress), null, new Random().Next(1, 15000), 15000);
But this did not calling periodically when server has load.
It is showing below log in serilog:
Starting HttpMessageHandler cleanup cycle with {InitialCount} items
Ending HttpMessageHandler cleanup cycle after {ElapsedMilliseconds}ms - processed: {DisposedCount} items - remaining: {RemainingItems} items
To handle async periodic callback, I would use System.Threading.PeriodicTimer. This way the execution of the next UpdateActivityAsync will not begin until the last one is done. If you still face thread pool starvation issue, you could manually create an additional thread in which you run the timer.
class Example {
private PeriodicTimer _activityTimer;
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_activityTimer.Dispose();
}
private async Task StartTimerLoopAsync() {
_activityTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(15000));
while (await _activityTimer.WaitForNextTickAsync()) {
await UpdateActivityAsync(_ipAddress);
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress) {
await Task.Delay(500); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE for .NET Core 3.1
Instead of PeriodicTimer you could simply use Task.Delay (it's not very accurate, but good enough for your use case I believe):
class Example {
private CancellationTokenSource _cancellationTokenSource = new ();
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(new Random().Next(1, 15000)); // System.Threading.Timer first delay
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(15000, _cancellationTokenSource.Token);
await UpdateActivityAsync(_ipAddress, _cancellationTokenSource.Token);
await delayTask;
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(1500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE
You could also create your own async Timer:
class Example : IDisposable {
private AsyncTimer<Example>? _timer;
public IPAddress IpAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
if (_timer is not null) {
return;
}
_timer = new AsyncTimer<Example>(async (state, ct) => await UpdateActivityAsync(state.IpAddress, ct), this, new Random().Next(1, 15000), 15000);
}
public void StopTimer() {
_timer?.Stop();
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
public void Dispose() {
_timer?.Dispose();
}
}
class AsyncTimer<T> : IDisposable {
public delegate Task AsyncTimerDelegate(T state, CancellationToken cancellationToken);
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly AsyncTimerDelegate _timerCallback;
private readonly TimeSpan _dueTime;
private readonly TimeSpan _interval;
private readonly T _state;
public AsyncTimer(AsyncTimerDelegate timerCallback, T state, int dueTime, int interval) {
_timerCallback = timerCallback;
_state = state;
_dueTime = TimeSpan.FromMilliseconds(dueTime);
_interval = TimeSpan.FromMilliseconds(interval);
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void Stop() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(_dueTime);
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(_interval, _cancellationTokenSource.Token);
await _timerCallback.Invoke(_state, _cancellationTokenSource.Token);
await delayTask;
}
}
public void Dispose() {
_cancellationTokenSource.Dispose();
}
}
I'm starting to learn about async / await in C# 5.0, and I don't understand it at all. I don't understand how it can be used for parallelism. I've tried the following very basic program:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Task task1 = Task1();
Task task2 = Task2();
Task.WaitAll(task1, task2);
Debug.WriteLine("Finished main method");
}
public static async Task Task1()
{
await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
Debug.WriteLine("Finished Task2");
}
}
}
This program just blocks on the call to Task.WaitAll() and never finishes, but I am not understanding why. I'm sure I'm just missing something simple or just don't have the right mental model of this, and none of the blogs or MSDN articles that are out there are helping.
I recommend you start out with my intro to async/await and follow-up with the official Microsoft documentation on TAP.
As I mention in my intro blog post, there are several Task members that are holdovers from the TPL and have no use in pure async code. new Task and Task.Start should be replaced with Task.Run (or TaskFactory.StartNew). Similarly, Thread.Sleep should be replaced with Task.Delay.
Finally, I recommend that you do not use Task.WaitAll; your Console app should just Wait on a single Task which uses Task.WhenAll. With all these changes, your code would look like:
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
}
public static async Task MainAsync()
{
Task task1 = Task1();
Task task2 = Task2();
await Task.WhenAll(task1, task2);
Debug.WriteLine("Finished main method");
}
public static async Task Task1()
{
await Task.Delay(5000);
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Delay(10000);
Debug.WriteLine("Finished Task2");
}
}
Understand C# Task, async and await
C# Task
Task class is an asynchronous task wrapper. Thread.Sleep(1000) can stop a thread running for 1 second. While Task.Delay(1000) won't stop the current work. See code:
public static void Main(string[] args){
TaskTest();
}
private static void TaskTest(){
Task.Delay(5000);
System.Console.WriteLine("task done");
}
When running," task done" will show up immediately. So I can assume that every method from Task should be asynchronous. If I replace TaskTest () with Task.Run(() =>TaskTest()) task done won't show up at all until I append a Console.ReadLine(); after the Run method.
Internally, Task class represent a thread state In a State Machine. Every state in state machine have several states such as Start, Delay, Cancel, and Stop.
async and await
Now, you may wondering if all Task is asynchronous, what is the purpose of Task.Delay ? next, let's really delay the running thread by using async and await
public static void Main(string[] args){
TaskTest();
System.Console.WriteLine("main thread is not blocked");
Console.ReadLine();
}
private static async void TaskTest(){
await Task.Delay(5000);
System.Console.WriteLine("task done");
}
async tell caller, I am an asynchronous method, don't wait for me. await inside the TaskTest() ask for waiting for the asynchronous task. Now, after running, program will wait 5 seconds to show the task done text.
Cancel a Task
Since Task is a state machine, there must be a way to cancel the task while task is in running.
static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static void Main(string[] args){
TaskTest();
System.Console.WriteLine("main thread is not blocked");
var input=Console.ReadLine();
if(input=="stop"){
tokenSource.Cancel();
System.Console.WriteLine("task stopped");
}
Console.ReadLine();
}
private static async void TaskTest(){
try{
await Task.Delay(5000,tokenSource.Token);
}catch(TaskCanceledException e){
//cancel task will throw out a exception, just catch it, do nothing.
}
System.Console.WriteLine("task done");
}
Now, when the program is in running, you can input "stop" to cancel the Delay task.
Your tasks never finish because they never start running.
I would Task.Factory.StartNew to create a task and start it.
public static async Task Task1()
{
await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
Debug.WriteLine("Finished Task2");
}
As a side note, if you're really just trying to pause in a async method, there's no need to block an entire thread, just use Task.Delay
public static async Task Task1()
{
await Task.Delay(TimeSpan.FromSeconds(5));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Delay(TimeSpan.FromSeconds(10));
Debug.WriteLine("Finished Task2");
}
Async and await are markers which mark code positions from where control should resume after a task (thread) completes.
Here's a detail youtube video which explains the concept in a demonstrative manner http://www.youtube.com/watch?v=V2sMXJnDEjM
If you want you can also read this coodeproject article which explains the same in a more visual manner.
http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features#Feature1:-“Async”and“Await”(Codemarkers)
static void Main(string[] args)
{
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "Main";
Console.WriteLine(Thread.CurrentThread.Name + "1");
TaskTest();
Console.WriteLine(Thread.CurrentThread.Name + "2");
Console.ReadLine();
}
private async static void TaskTest()
{
Console.WriteLine(Thread.CurrentThread.Name + "3");
await Task.Delay(2000);
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "FirstTask";
Console.WriteLine(Thread.CurrentThread.Name + "4");
await Task.Delay(2000);
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "SecondTask";
Console.WriteLine(Thread.CurrentThread.Name + "5");
}
If you run this program you will see that await will use different thread. Output:
Main1
Main3
Main2
FirstTask4 // 2 seconds delay
SecondTask5 // 4 seconds delay
But if we remove both await keywords, you will learn that async alone doesn't do much. Output:
Main1
Main3
Main4
Main5
Main2
I was creating a puzzle with a bit of information in different sources to create this...
System.Threading.Thread th;
th = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
lbMemFree.Text = "memory free: " + NSProcessInfo.ProcessInfo.PhysicalMemory; // this works!
});
}));
th.Start();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
th.Sleep(500); // delay execution for 500 ms
// more code
});
The idea is to create something that update the label times in time. In this scenario: 500ms.
But the th.Sleep(500) don't allow the app to compile. It's says: Error CS0176: Static member System.Threading.Thread.Sleep(int) cannot be accessed with an instance reference, qualify it with a type name instead (CS0176).
You can use async await for this.
Interval
public class Interval
{
public static async Task SetIntervalAsync(Action action, int delay, CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
await Task.Delay(delay, token);
action();
}
}
catch(TaskCanceledException) { }
}
}
usage (e.g. Console Application for demo)
class Program
{
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
Interval.SetIntervalAsync(DoSomething, 1000, cts.Token);
Console.ReadKey(); // cancel after first key press.
cts.Cancel();
Console.ReadKey();
}
public static void DoSomething()
{
Console.WriteLine("Hello World");
}
}
Use the CancellationTokenSource to cancel the execution of the interval.
I have a job that run every X seconds that shouldn't take long to execute (less than 5 seconds) that is ran in a WindowsService. My goal is to ensure that a currently running job will complete it's execution if the Windows Service is stopped in the middle of it's execution.
This thread seemed interesting, but it doesn't work so far:
Task Handling on Application Shutdown
Any help will be appreciated.
WindowsService implementation:
public partial class WindowsService : ServiceBase
{
private readonly ILogger logger;
private readonly IStartJob startJob;
private readonly CancellationTokenSource cancellationTokenSource;
private Task task;
public WindowsService(IStartJob startJob, ILogger logger)
{
// Dependancy injection of logger and JobStarter
this.startJob = startJob;
this.logger = logger;
InitializeComponent();
cancellationTokenSource = new CancellationTokenSource();
}
protected override void OnStart(string[] args)
{
task = new Task(() => startJob.Start(cancellationTokenSource.Token),
cancellationTokenSource.Token, TaskCreationOptions.LongRunning);
task.Start();
logger.Info("Service started");
}
protected override void OnStop()
{
RequestAdditionalTime(TimeSpan.FromSeconds(30).Milliseconds);
cancellationTokenSource.Cancel();
try { task.Wait(); }
catch (Exception ex) { logger.Error("Error while stopping the Service", ex); }
logger.Info("Service stopped");
}
}
StartJob Implementation:
public class StartJob : IStartJob
{
private readonly ILogger logger;
private readonly IExecuteJob executeJob;
private DateTime lastExecution = DateTime.UtcNow.AddDays(-1);
public StartJob(ILogger logger, IExecuteJob executeJob)
{
this.logger = logger;
this.executeJob = executeJob;
}
public async void Start(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var nextExecution = lastExecution.AddSeconds(5);
if (nextExecution < DateTime.UtcNow)
{
try
{
logger.Info("Start Job Execution");
// To similate long process not ended, break point here
// and stop the service before continuing the execution.
Thread.Sleep(5000);
executeJob.Execute();
logger.Info("Job completed");
AdjustLastExecution(nextExecution);
}
catch (Exception ex)
{
logger.Error("Unexpected exception while executing job", ex);
}
}
// Would expect the cancelToken to activate here or in the while condition
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
private void AdjustLastExecution(DateTime nextExecution)
{
// To ensure we have an heartbeat close to 5 seconds
lastExecution = nextExecution.AddSeconds(5) > DateTime.UtcNow ?
nextExecution : DateTime.UtcNow.AddSeconds(-5);
}
}
I tried a bunch of variations with the cancelToken present in the entry task, task wait and wait millisecondsTimeout, but nothing did the trick so far.
I attached my solution to the executing WindowsService and ensure that the executing job would be in the middle of it with break points and Thread.Sleep. But the Job completed log isn't written and the attached processed is dropped before it.
I shortly looked at your code and only thing I would try is
to replace :
async void
with
async Task
I don't guarantee that it applies to your case, but I recalled reading this article recommending to avoid async void.
In C#, async void methods are a scourge upon your code..
Haacked.com | Avoid async void methods
EDIT :
task = new Task(() => startJob.Start(cancellationTokenSource.Token),
cancellationTokenSource.Token, TaskCreationOptions.LongRunning);
with
task = startJob.Start(cancellationTokenSource.Token);
I am currently trying to implement a substitute for .Net 4.5's Task.Delay() method in a program that must target .Net 4.0. I found the following code at this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks are fairly new to me. System.Threading.Timer and TaskCompletionSource are brand new to me (as of today), and I'm struggling a bit with them. All that aside, I'm wondering how I might add CancellationToken functionality to this code. I'm assuming I could add a parameter to the Delay() method like this:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
... but then, where do I put the logic for checking the token and getting out of the method? Somewhere in the callback? Is this even possible?
I've tried to change your code as little as possible but here is a working example that behaves in the same way as Task.Delay.
It's important to note that I use TrySetCanceled and TrySetResult because the Timer could finish after the task is canceled. Ideally you want to stop the timer.
Also note a canceled task will throw a TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
Reading a bit more into your question. If you need Task.Delay and you're targeting .NET 4.0 then you should use the Microsoft Async nuget package from http://www.nuget.org/packages/Microsoft.Bcl.Async/ it contains the method TaskEx.Delay
Like this:
token.Register(() => tcs.TrySetCancelled());
Here you are a version that prevents disposal of timer by the garbage collector
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}