In my project I'm using ASP.NET Core 3.1 with a hosted service background worker.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var result = await _messageBus.Get();
if (result != null)
{
await _dbContext.UpdateData(result, stoppingToken);
}
await Task.Delay(5000, stoppingToken);
}
}
Inside my DbContext I do some logic then call await command.ExecuteNonQueryAsync(stoppingToken);. On that line the worker deadlocks.
await using var connection = new SqlConnection(dbConnection);
await connection.OpenAsync(stoppingToken);
var command = connection.CreateCommand();
command.CommandText = query;
await command.ExecuteNonQueryAsync(stoppingToken);
I then changed the background worker to:
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(async state => await Run(state, stoppingToken), null,
TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private async Task Run(object state, CancellationToken stoppingToken)
{
var result = await _messageBus.Get();
if (result != null) await _dbContext.UpdateData(result, stoppingToken);
}
This ended up working and avoided a deadlock. However I honestly don't know why this worked and avoided a deadlock. What makes the the Timer class different than just using Task.Delay?
I strongly suspect that your code is not actually asynchronous, which will cause startup issues (that can look like deadlocks). It's not documented well, but ExecuteAsync must be asynchronous. So if you have blocking code at the beginning of that method (e.g., if the "get message from the message bus" code is actually synchronously blocking until a message is received), then you'll need to wrap it in a Task.Run:
protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () =>
{
while (!stoppingToken.IsCancellationRequested)
{
var result = await _messageBus.Get();
if (result != null)
{
await _dbContext.UpdateData(result, stoppingToken);
}
await Task.Delay(5000, stoppingToken);
}
});
In my own code, I use a separate base type to do the Task.Run so it's less ugly.
Related
I have an ASP.Net Core Web API application which consumes messages from an AMQ Queue. Currently I have the consuming code in a BackgroundService with an event handler hooked up to the Listener. The whole thing is in a while look (checking the cancellation token) to ensure any errors are handled and we retry the subscription but I also have an inner while loop to keep the service alive but it doesn't need to do anything.
My question is, what should I do inside that inner while loop to make sure I don't consume unnecessary CPU; e.g. Task.Yield(), Task.Delay(something)?
public class ReceiverService : BackgroundService
{
...
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
...
while (!stoppingToken.IsCancellationRequested)
{
...
IConnectionFactory factory =
new NMSConnectionFactory(
$"activemq:ssl://{parsed?["message"]}:51513?wireFormat.maxInactivityDuration=0");
connection = await factory.CreateConnectionAsync(Username, Password);
var session = await connection.CreateSessionAsync();
var destination = await session.GetQueueAsync("queuename/" + subscriptionId);
var consumer = await session.CreateConsumerAsync(destination);
consumer.Listener += async message =>
{
// do stuff with message
message.Acknowledge();
};
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(0, stoppingToken);
}
await connection?.CloseAsync()!;
await Task.Delay(1000, stoppingToken);
}
}
}
Cheers
Rich
If you have nothing but cleanup to do, then you can just do await Task.Delay(Timeout.InfiniteTimeSpan, stoppingToken);. No need for any loops at all.
I'm playing with these Windows 8 WinRT tasks, and I'm trying to cancel a task using the method below, and it works to some point. The CancelNotification method DOES get called, which makes you think the task was cancelled, but in the background the task keeps running, then after it's completed, the status of the Task is always completed and never cancelled. Is there a way to completely halt the task when it's cancelled?
private async void TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.Token.Register(CancelNotification);
source.CancelAfter(TimeSpan.FromSeconds(1));
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);
await task;
if (task.IsCompleted)
{
MessageDialog md = new MessageDialog(task.Result.ToString());
await md.ShowAsync();
}
else
{
MessageDialog md = new MessageDialog("Uncompleted");
await md.ShowAsync();
}
}
private int slowFunc(int a, int b)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
}
return a + b;
}
private void CancelNotification()
{
}
Read up on Cancellation (which was introduced in .NET 4.0 and is largely unchanged since then) and the Task-Based Asynchronous Pattern, which provides guidelines on how to use CancellationToken with async methods.
To summarize, you pass a CancellationToken into each method that supports cancellation, and that method must check it periodically.
private async Task TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);
// (A canceled task will raise an exception when awaited).
await task;
}
private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return a + b;
}
Or, in order to avoid modifying slowFunc (say you don't have access to the source code for instance):
var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code
//original code: await task;
await Task.WhenAny(task, completionSource.Task); //New code
You can also use nice extension methods from https://github.com/StephenCleary/AsyncEx and have it looks as simple as:
await Task.WhenAny(task, source.Token.AsTask());
One case which hasn't been covered is how to handle cancellation inside of an async method. Take for example a simple case where you need to upload some data to a service get it to calculate something and then return some results.
public async Task<Results> ProcessDataAsync(MyData data)
{
var client = await GetClientAsync();
await client.UploadDataAsync(data);
await client.CalculateAsync();
return await client.GetResultsAsync();
}
If you want to support cancellation then the easiest way would be to pass in a token and check if it has been cancelled between each async method call (or using ContinueWith). If they are very long running calls though you could be waiting a while to cancel. I created a little helper method to instead fail as soon as canceled.
public static class TaskExtensions
{
public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
{
token.ThrowIfCancellationRequested();
await Task.WhenAny(task, token.WhenCanceled());
token.ThrowIfCancellationRequested();
return await task;
}
public static Task WhenCanceled(this CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
So to use it then just add .WaitOrCancel(token) to any async call:
public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
Client client;
try
{
client = await GetClientAsync().WaitOrCancel(token);
await client.UploadDataAsync(data).WaitOrCancel(token);
await client.CalculateAsync().WaitOrCancel(token);
return await client.GetResultsAsync().WaitOrCancel(token);
}
catch (OperationCanceledException)
{
if (client != null)
await client.CancelAsync();
throw;
}
}
Note that this will not stop the Task you were waiting for and it will continue running. You'll need to use a different mechanism to stop it, such as the CancelAsync call in the example, or better yet pass in the same CancellationToken to the Task so that it can handle the cancellation eventually. Trying to abort the thread isn't recommended.
I just want to add to the already accepted answer. I was stuck on this, but I was going a different route on handling the complete event. Rather than running await, I add a completed handler to the task.
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
Where the event handler looks like this
private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
if (status == AsyncStatus.Canceled)
{
return;
}
CommentsItemsControl.ItemsSource = Comments.Result;
CommentScrollViewer.ScrollToVerticalOffset(0);
CommentScrollViewer.Visibility = Visibility.Visible;
CommentProgressRing.Visibility = Visibility.Collapsed;
}
With this route, all the handling is already done for you, when the task is cancelled it just triggers the event handler and you can see if it was cancelled there.
I have interactive task which in "worst" scenario is not executed at all, thus it is represented by TaskCompletionSource.
I would like to wait for either this task completes, or token which I received is cancelled -- whichever happens first. Perfect tool for such job would be Task.WhenAny, the only problem is it takes only tasks, and I have one Task and one CancellationToken.
How to wait (asynchronously, like Task.WhenAny) for the first event triggered -- completed task, or cancelled token?
async Task MyCodeAsync(CancellationToken token)
{
var tcs = new TaskCompletionSource<UserData>(); // represents interactive part
await Task.WhenAny(tcs.Task, token); // imaginary call
UserData data = tcs.Task.Result; // user interacted, let's continue
...
}
I don't create/manage token, so I cannot change it. I have to deal with it.
Update: For such particular case one could use Register method on token to cancel the TaskCompletionSource. For more general method please see Matthew Watson answer.
You could just create an extra task that returns when the cancel token's wait handle is signalled:
var factory = new CancellationTokenSource();
var token = factory.Token;
await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
myTask());
(However, be aware that this - while simple - does use up an extra thread, which is clearly not ideal. See later for an alternate solution which doesn't use an extra thread.)
If you want to check which task completed, you will have to keep a copy of the tasks before calling WhenAny() so you can compare them to the return value, for example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000); // Change to 3000 for different result.
var token = factory.Token;
var task = myTask();
var result = await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
task);
if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}
static async Task myTask()
{
await Task.Delay(2000);
}
}
}
If you don't want to waste an entire thread waiting for the cancellation token to be signalled, you can use CancellationToken.Register() to register a callback with which you can set the result of a TaskCompletionSource:
(Lifted from here)
public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}
You can then use that as follows:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000);
var token = factory.Token;
var task = myTask();
var result = await Task.WhenAny(
WhenCanceled(token),
task);
if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}
public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}
static async Task myTask()
{
await Task.Delay(2000);
}
}
}
This is a preferable approach for the general case.
With this scenario, you have to be extremely careful about leaks. In particular, having objects referenced by delegates that are registered to a long-lived CancellationToken.
The approach that I eventually ended up with in my AsyncEx library looks like this:
public static async Task<T> WaitAsync<T>(this Task<T> task, CancellationToken token)
{
var tcs = new TaskCompletionSource<T>();
using (token.Register(() => tcs.TrySetCanceled(token), useSynchronizationContext: false)
return await await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
}
The code above ensures that the registration is disposed if the CancellationToken is not canceled.
Usage:
async Task MyCodeAsync(CancellationToken token)
{
UserData data = await userDataTask.WaitAsync(token);
}
Here is an extension method that transforms a CancellationToken to a Task or Task<TResult>. The returned task will complete as cancelled immediately after the CancellationToken receives a cancellation request.
static class CancellationTokenExtensions
{
public static Task AsTask(this CancellationToken token)
{
return new Task(() => throw new InvalidOperationException(), token);
}
public static Task<TResult> AsTask<TResult>(this CancellationToken token)
{
return new Task<TResult>(() => throw new InvalidOperationException(), token);
}
}
Usage example. Just await any task:
await Task.WhenAny(tcs.Task, token.AsTask());
...or await and get the result in the same line as well:
var data = await Task.WhenAny(tcs.Task, token.AsTask<UserData>()).Unwrap();
The InvalidOperationException is thrown just in case, to ensure that the task of the CancellationToken will never run to completion. Its Status can only be Created, Canceled or Faulted.
I'm playing with these Windows 8 WinRT tasks, and I'm trying to cancel a task using the method below, and it works to some point. The CancelNotification method DOES get called, which makes you think the task was cancelled, but in the background the task keeps running, then after it's completed, the status of the Task is always completed and never cancelled. Is there a way to completely halt the task when it's cancelled?
private async void TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.Token.Register(CancelNotification);
source.CancelAfter(TimeSpan.FromSeconds(1));
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);
await task;
if (task.IsCompleted)
{
MessageDialog md = new MessageDialog(task.Result.ToString());
await md.ShowAsync();
}
else
{
MessageDialog md = new MessageDialog("Uncompleted");
await md.ShowAsync();
}
}
private int slowFunc(int a, int b)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
}
return a + b;
}
private void CancelNotification()
{
}
Read up on Cancellation (which was introduced in .NET 4.0 and is largely unchanged since then) and the Task-Based Asynchronous Pattern, which provides guidelines on how to use CancellationToken with async methods.
To summarize, you pass a CancellationToken into each method that supports cancellation, and that method must check it periodically.
private async Task TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);
// (A canceled task will raise an exception when awaited).
await task;
}
private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return a + b;
}
Or, in order to avoid modifying slowFunc (say you don't have access to the source code for instance):
var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code
//original code: await task;
await Task.WhenAny(task, completionSource.Task); //New code
You can also use nice extension methods from https://github.com/StephenCleary/AsyncEx and have it looks as simple as:
await Task.WhenAny(task, source.Token.AsTask());
One case which hasn't been covered is how to handle cancellation inside of an async method. Take for example a simple case where you need to upload some data to a service get it to calculate something and then return some results.
public async Task<Results> ProcessDataAsync(MyData data)
{
var client = await GetClientAsync();
await client.UploadDataAsync(data);
await client.CalculateAsync();
return await client.GetResultsAsync();
}
If you want to support cancellation then the easiest way would be to pass in a token and check if it has been cancelled between each async method call (or using ContinueWith). If they are very long running calls though you could be waiting a while to cancel. I created a little helper method to instead fail as soon as canceled.
public static class TaskExtensions
{
public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
{
token.ThrowIfCancellationRequested();
await Task.WhenAny(task, token.WhenCanceled());
token.ThrowIfCancellationRequested();
return await task;
}
public static Task WhenCanceled(this CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
So to use it then just add .WaitOrCancel(token) to any async call:
public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
Client client;
try
{
client = await GetClientAsync().WaitOrCancel(token);
await client.UploadDataAsync(data).WaitOrCancel(token);
await client.CalculateAsync().WaitOrCancel(token);
return await client.GetResultsAsync().WaitOrCancel(token);
}
catch (OperationCanceledException)
{
if (client != null)
await client.CancelAsync();
throw;
}
}
Note that this will not stop the Task you were waiting for and it will continue running. You'll need to use a different mechanism to stop it, such as the CancelAsync call in the example, or better yet pass in the same CancellationToken to the Task so that it can handle the cancellation eventually. Trying to abort the thread isn't recommended.
I just want to add to the already accepted answer. I was stuck on this, but I was going a different route on handling the complete event. Rather than running await, I add a completed handler to the task.
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
Where the event handler looks like this
private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
if (status == AsyncStatus.Canceled)
{
return;
}
CommentsItemsControl.ItemsSource = Comments.Result;
CommentScrollViewer.ScrollToVerticalOffset(0);
CommentScrollViewer.Visibility = Visibility.Visible;
CommentProgressRing.Visibility = Visibility.Collapsed;
}
With this route, all the handling is already done for you, when the task is cancelled it just triggers the event handler and you can see if it was cancelled there.
I have the following extension which allows me to force a timeout on a task.
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeSpan, string message = null)
{
if (task == await Task.WhenAny(task, Task.Delay(timeSpan)))
return await task;
else
{
throw new TimeoutException(message);
}
}
I have the following Task:
private async Task<string> TestTask(PageData page)
{
await Task.Delay(TimeSpan.FromSeconds(10));
return "teststring";
}
When I call the task using the following I get the intended result.
var teststring = await TestTask()
.TimeoutAfter(TimeSpan.FromSeconds(5), "timeout message");
I issue I am having is that when I call a method in an external library (in this case a c++ CLR Class library), the code inside TimeoutAfter() is never reached and the task never timeouts. I can fix the issue by using Task.Factory.StartNew() (below), passing a callback to the long running task and creating my Task.Delay task before exectution of the main task starts, but this won't work as an extension.
private async Task<T> Timeout<T>(Func<Task<T>> taskFunc)
{
var taskWait = Task.Delay(TimeSpan.FromSeconds(15));
var task = Task.Factory.StartNew(() => taskFunc());
if (task == await Task.WhenAny(task, taskWait))
return await (await task);
else
{
throw new TimeoutException("timeout");
}
}
Why does it appear that the code in TimeoutAfter() is only executing when certain content is in the task it is extending? What functionality can I add to the extension to prevent this from happening?
I think your main issue is that you just don't even start your task when you try with extension method.
A Task is fully async and must be started to reach its code. One way to start a task is using StartNew as you tried with the factory.
But you can also just call the Start method. The only thing you have to be warned is that if a start has already been started or is already finished, you will get an exception when calling Start.
So, I think your solution can be :
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeSpan, string message = null)
{
if(task.Status != TaskStatus.Running && task.Status != TaskStatus.RanToCompletion){
task.Start();
}
if (task == await Task.WhenAny(task, Task.Delay(timeSpan)))
return await task;
else
{
throw new TimeoutException(message);
}
}