How to cancel async Task after a period of time - c#

In my Windows Store app I have a method
public async static Task InitAds()
{
Debug.WriteLine("API: Loading Ad images");
await Task.WhenAll(ads.Select(l => l.Value).Where(l=>l!=null).Select(l => l.StartRotation()));
}
I use to download and initialize (download, parse| Ads in a project. This method is awaited when called
...
await AdReader.InitAds()
...
The problem is that Ads server sometimes responds very slowly. I want to have a timeout, say 10 seconds for this method to run. If it does not finish in this timeout, I want it to be killed and my code to continue.
What is the best way to implement this? I found How to cancel a Task in await? but it uses a TaskFactory and when I try that approach and call my method in Task.Run it is not awaited and the code continues.
Edit:
The StartRotation is also an async method calling another async methods
public async Task StartRotation(CancellationToken ct)
{
if (Images.Count == 1)
{
await Image.LoadAndSaveImage(ct);
}
if (Images.Count <2) return;
foreach (var img in Images)
{
await img.LoadAndSaveImage(ct);
}
Delay = Image.Delay;
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(Delay);
dt.Tick += (s, e) =>
{
++index;
if (index > Images.Count - 1)
{
index = 0;
}
Image = Images[index];
};
dt.Start();
}

Cancellation is cooperative. You just need to pass CancellationToken into your StartRotation:
public async static Task InitAds(CancellationToken token)
{
Debug.WriteLine("API: Loading Ad images");
await Task.WhenAll(ads.Select(l => l.Value).Where(l=>l!=null).Select(l => l.StartRotation(token)));
}
And then call it as such:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await InitAds(cts.Token);

Related

C# How to await async task until it indicates to proceed

I have a C# method, which calls an external web service multiple times, in a loop. I need to call it asynchronously in a different thread.
But the caller process MUST wait until the async thread meets a certain condition, - this condition occurs much before the loop iterations complete.
Please suggest a C# code example which describes how to wait until the async block of code indicates that a certain condition has been met, so that the main process can proceed without waiting for loop to finish.
My code:
..
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Factory.StartNew(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
}
}
});
// Do other stuff...
You could execute your method as fire and forget and then wait for a TaskCompletionSource. This TaskCompletionSource is given to the method that calls the webservice as parameter. The method then sets a result on the TaskCompletionSource at some point.
Here is an example code piece:
public async Task DoWebserviceStuffAsync(TaskCompletionSource<bool> taskCompletionSource)
{
for (int i = 0; i < 10; i++)
{
//your webservice call
await Task.Delay(5000);
//some condition
if (i == 1)
{
//after setting this your CallingMethod finishes
//waiting the await taskCompletionSource.Task;
taskCompletionSource.TrySetResult(true);
}
}
}
private async Task CallerMethod()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
//call method without await
//care: You cannot catch exceptions without await
DoWebserviceStuffAsync(taskCompletionSource);
//wait for the DoWebserviceStuffAsync to set a result on the passed TaskCompletionSource
await taskCompletionSource.Task;
}
If you want to avoid the danger of "fire and forget" or you also need to wait for the full operation to complete, you could return two tasks (Task,Task) (C# v7 syntax). The caller would await both tasks in order.
public async Task Caller()
{
var (partOne,partTwo) = DoSomethingAsync();
await partOne;
//part one is done...
await partTwo;
//part two is done...
}
public (Task,Task) DoSomethingAsync()
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
return (tcs.Task, DoWork(tcs));
}
public async Task DoWork(TaskCompletionSource<bool> tcs)
{
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Run(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
tcs.SetResult(true);
}
}
});
// Do other stuff...
}

C# Task wait and timeout

I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application.
internal void ReadAll()
{
var data = new byte[1];
Task.Factory.StartNew(() =>
{
var ct = new CancellationTokenSource();
var ReadAllTask = Task.Factory.StartNew(() =>
{
// Read all information
// [omit communication exchange via COM port]
ct.Cancel();
}, ct.Token);
// First thread waiting 10s for the worker to finish
ReadAllTask.Wait(10000, ct.Token);
if (ReadAllTask.Status == TaskStatus.RanToCompletion)
{
ReadAllComplete?.Invoke(true);
}
else
{
ct.Cancel();
ReadAllComplete?.Invoke(false);
}
});
}
This method is called by pressing a button. It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown.
Your code could be a lot simpler than current version. Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property.
Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. async/await mechanism will invoke the event in UI context, so code could be something like this:
// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
var data = new byte[2];
// cancel source after 10 seconds
var cts = new CancellationTokenSource(10000);
// Read all information
// [omit communication exchange via COM port]
// async operation with BaseStream
var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);
/*
* if you can't use the BaseStream methods, simply call this method here
* cts.Token.ThrowIfCancellationRequested();
*/
// this code would run only if everything is ok
// check result here in your own way
var boolFlag = result != null;
ReadAllComplete?.Invoke(boolFlag);
}
Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. If at all possible you should switch to a true async API and drop the Task.Run.
private CancellationTokenSource cts;
public async void MyButtonhandler(object sender, EventArgs e) {
cts = new CancellationTokenSource();
try {
var result = await Task.Run(() => ReadAll(cts));
if (result) {
//success
} else {
//failure
}
} catch (TaskCanceledException ex) {
}
}
internal async Task<bool> ReadAll(CancellationTokenSource cts) {
byte[] data = new byte[1];
var timeout = TimeSpan.FromSeconds(10);
var ReadAllTask = Task.Run(() => {
// Read all information
// [omit communication exchange via COM port]
}, cts.Token);
if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
return true;
}
cts.Cancel();
return false;
}
Reading comments and answers to my question I learned a couple of useful things that solve my problem:
CancellationTokenSource can have an implicit timeout
use Task.Run instead Task.Factory.StartNew
don't need to cancel the task, the cts will do the work
Now my code is simpler and it works:
private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
// Start timedout task that will send all necessary commands
CancellationTokenSource cts = new CancellationTokenSource(10000);
Task.Run(() =>
{
oCommandSets.ReadAll(cts);
}, cts.Token);
}
and
internal void ReadAll(CancellationTokenSource cts)
{
// [communication]
if (cts.IsCancellationRequested)
{
ReadAllComplete?.Invoke(false);
}
else
{
ReadAllComplete?.Invoke(true);
}
}
In any case I need to learn more about multithreading.

how to cancel a function after a while?

I want to know how can I cancel a function after a certain time!
for example, how can I cancel this function?
private async Task function()
{
try
{
while (true)
{
//mycode
}
}
catch{ }
}
how can I cancel this function?
Normally, awaitable methods will take a CancellationToken, so you'd just pass it on through:
private async Task functionAsync(CancellationToken cancellationToken)
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); //mycode
}
}
Some time ago i faced the same issue and found a pretty good solution working for me. I am separating async calls to another service via wcf this way (see my code below) by doing two things to cancel after a maximum time:
You can use CancellationToken in combination with a second Task to run parallel to your running task and cancel it if necessary:
private const int TimeOut = 50000;
public static async Task<T> HandleServiceCall<T>(Func<Task<T>> doServiceCall, CancellationTokenSource source) where T : class
{
var delaySource = new CancellationTokenSource(TimeSpan.FromSeconds(50));
source.Token.ThrowIfCancellationRequested();
var res = doServiceCall();
if (await Task.WhenAny(res, Task.Delay(TimeSpan.FromMilliseconds(TimeOut), delaySource.Token)) == res)
{
delaySource.Cancel();
await res;
}
else
{
source.Cancel();
throw new Exception("Your Text");
}
return await res;
}
You can call this Method for example this way:
var source = new CancellationTokenSource(TimeSpan.FromSeconds(50));
source.Token.ThrowIfCancellationRequested();
MyWrapperClass.HandleServiceCall(async () => await MyAsyncMethod(source.Token), source).Result
To clarify what is done here:
I am creating a cancellation Token for my Task with a given max TimeSpan and then give this Token to the async Method which should be called.
This call is given as a func into my HandleServiceCall Method.
This Method will create another CancellationToken with a given greater TimeSpan, which will Run as a delayed Task (task.Delay will just wait until the Token is triggered).
Task.WhenAny will look if the normal async task or the delayed task is finishing first. If it is the delayed one, your maximum time has expired and an exception is thrown.
Greetings
Konstantin
You could use the Timer class, start it when you want it to (beginning of the program presumably), and use some like a simple if statement to stop it, such as *if timer is greater than set time, end program. *
I add small example
static CancellationTokenSource cts;
static void Main(string[] args)
{
cts = new CancellationTokenSource();
Task.Factory.StartNew(test);
cts.Cancel();
}
private async static void test()
{
await function(cts.Token);
}
static async Task function(CancellationToken ct)
{
try
{
while (!ct.IsCancellationRequested)
{
Thread.Sleep(1000);
//mycode
}
}
catch { }
}

Can the Elapsed callback of a System.Timers.Timer be async?

Is it possible (or even reasonable) to make the callback of a System.Timers.Timer an async method? Something like:
var timer = new System.Timers.Timer
{
Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();
It compiles (obviously a good place to start), but I'm not sure I understand the consequences. Will the timer await the callback before resetting the timer?
Will the timer await the callback before resetting the timer?
No. There's nothing it could await, because the signature of ElapsedEventHandler has a void return type.
In other words, your code is equivalent to:
var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();
...
private async void Foo()
{
...
}
Whether that's acceptable for you or not will depend on your context. In general, having async void methods or anonymous functions makes them harder to test and reuse - but the ability was precisely given for the sake of event handlers... You should consider how errors will be propagated though.
The title of the question is specifically about Timers, but if we look at it as "How to call an async method after some time?" then you could do it without using a timer.
var task2 = Task.Run(async () => {
while (true)
{
try
{
await MyMethod2();
} catch
{
//super easy error handling
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod2()
{
//async work here
}
Please note however that this will have different timing (timer will be called at an interval, the code above will be called every (run time + sleep_time), but even if MyMethod2 takes a long time it it won't be called twice. Having said that, you can calculate how long to await for to run 'every x minutes'.
Actually, you can.
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
The solution that proposed #tymtam doesn´t wait until MyMethod2 has ended.
I think it would be better to use this. An example with two async tasks, when both has finised, wait 5 seconds and execute again the two tasks:
var task2 = Task.Run(async () => {
while (true)
{
try
{
var task1 = MyMethod1();
var task2 = MyMethod2();
List<Task> allTasks = new List<Task> { task1, task2 };
while (allTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(allTasks);
if (finishedTask == task1)
{
Console.WriteLine("MyMethod1 has ended");
}
else if (finishedTask == task2)
{
Console.WriteLine("MyMethod2 has ended");
}
tareas.Remove(finishedTask);
}
//Here only when finished all task
} catch
{
//super easy error handling
}
//Wait until next cycle
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod1()
{
//async work here
}
public async Task MyMethod2()
{
//async work here
}

Pausing a task within Task.Factory.StartNew

In my Asp.Net MVC 5 project I have a ~3 minute task that I pass to Task.Factory.StartNew().
I would like to pause the task from within the task if there is a validation issue in one of the steps of my code running in the task. I don't want to delay it async because the rest of the task will continue to run, which can't happen.
Could I use thread.sleep() without any repercussions since I'm within a task? I read that I may have to use TaskScheduler.Default to have the Task.Factory create a new thread for each task.
I'm using a PauseToken similar to a CancellationToken so I'll be able to resume the task or cancel this task based on user input.
Multithreading really scares me, and I don't want to overlook something.
Here is an example of the Thread.Sleep implementation:
public void WaitIfPaused(PauseToken pauseToken, CancellationToken cancellationToken, IProgressBar progressBar)
{
//TODO: If paused for an hour notify user via noty and abort the task to keep it from completing by cancellation via cancellationToken.
//wait for 1 hour
for (int i = 0; i < 3600; i++)
{
ThrowExceptionIfCancelled(cancellationToken, progressBar);
if (pauseToken.IsPaused)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
}
PauseToken: http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
Requested: Implementation of task structure in shared code library.
public void StartTask(params object[] data)
{
//throw an exception if no ITask was found
if (_taskToRun == null)
throw new Exception("Task cannot be null");
//set up task cancellation
CancellationTokenSource = new CancellationTokenSource();
var cancellationToken = CancellationTokenSource.Token;
//set up task pausing
PauseTokenSource = new PauseTokenSource();
var pauseToken = PauseTokenSource.Token;
//start a new task using the Task that was set
_task = Task.Factory.StartNew(() => _taskToRun.Execute(cancellationToken, pauseToken, data), cancellationToken);
}
My Execute method that is invoked by _taskToRun.Execute:
Public override void Execute(CancellationToken cancellationToken, PauseToken pauseToken, params object[] data)
{
var managerList = (List<IFileManager>) data[0];
var instr = (List<InstructionSet>) data[1];
ProcessInstructions(managerList, instr, cancellationToken, pauseToken);
}
Update due to comments:
Code example: 3 instructions
For(var instruction in instructions)
{
instruction.Execute();
}
In my execute method I run into a scenario for pause and call WaitWhilePausedAsync from within the execute. It will continue to execute the other two instructions, but pause the only the current instructions execute method.
Edit: By awaiting instruction.Execute() it will wait until instruction.Execute() completes or is unpaused.
Final Edit:
I was able to resolve the issue by awaiting the Execute method and making it async like Servy and I3arnon suggested.
Final Code Sample:
foreach(var instruction in instructions)
{
try
{
await instruction.Execute(pauseToken);
}
catch(InvalidOperationException)
{
pauseTokenSource.IsPaused = true;
//ask if user wants to cancel or resume.
}
}
//Simplified
public async Task<bool> Execute(PauseToken pauseToken)
{
await pauseToken.WaitWhilePausedAsync();
//do work
}
You can safely use Thread.Sleep. The only drawback is that the thread would be wasted blocking synchronously.
You should be using await Task.Delay(1000) instead. The code after that line would not execute until the wait is complete, but you won't be wasting a thread in the meantime:
public async Task WaitIfPausedAsync(PauseToken pauseToken, CancellationToken cancellationToken, IProgressBar progressBar)
{
for (int i = 0; i < 3600; i++)
{
ThrowExceptionIfCancelled(cancellationToken, progressBar);
if (pauseToken.IsPaused)
{
await Task.Delay(1000)
}
else
{
break;
}
}
}
Edit: I was unaware of PauseToken.WaitWhilePausedAsync. You should definitly use that instead of replicating that yourself with polling over PauseToken.IsPaused

Categories

Resources