I have found a very confusing behavior of async methods. Consider the following Console app:
private static int _i = 0;
private static Task<int> _calculateTask = Task.FromResult(0);
private static int _lastResult = 0;
static void Main(string[] args)
{
while (true)
{
Console.WriteLine(Calculate());
}
}
private static int Calculate()
{
if (!_calculateTask.IsCompleted)
{
return _lastResult;
}
_lastResult = _calculateTask.Result;
_calculateTask = CalculateNextAsync();
return _lastResult;
}
private static async Task<int> CalculateNextAsync()
{
return await Task.Run(() =>
{
Thread.Sleep(2000);
return ++_i;
});
}
As expected, after it is launched, it first prints out a bunch of 0s, then ones, twos and so on.
In contrast consider the following UWP app snippet:
private static int _i = 0;
private static Task<int> _calculateTask = Task.FromResult(0);
private static int _lastResult = 0;
public int Calculate()
{
if (!_calculateTask.IsCompleted)
{
return _lastResult;
}
_lastResult = _calculateTask.Result;
_calculateTask = CalculateNextAsync();
return _lastResult;
}
private static async Task<int> CalculateNextAsync()
{
return await Task.Run( async() =>
{
await Task.Delay(2000);
return ++_i;
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
while( true)
{
Debug.WriteLine(Calculate());
}
}
Although these two differ only in one small detail, the UWP snippet just keeps printing out 0 and task state in the if statement just stays Waitingforactivation. Furthermore the problem can be fixed by removing async and await from CalculateNextAsync:
private static Task<int> CalculateNextAsync()
{
return Task.Run(async () =>
{
await Task.Delay(2000);
return ++_i;
});
}
Now everything works the same way as in the Console app.
Can someone explain the reason why the behavior in Console differs from UWP app? And why the task stays as c in case of UWP app?
Update
I have got back to this question again, but found out an issue that the originally accepted answer does not cover - the code on UWP never reaches the .Result, it just keeps checking for IsCompleted which returns false, hence the _lastResult is returned. What makes the Task have a AwaitingActivation state when it should have completed?
Solution
I figured out that the reason is that the active waiting while loop prevents the await continuation from ever seizing the UI thread again, hence causing a "deadlock"-like situation.
Based on the code in the UWP app there is no need for holding on to the _calculateTask. Just await the task.
Here is the updated code
private static int _i = 0;
private static int _lastResult = 0;
public async Task<int> Calculate() {
_lastResult = await CalculateNextAsync();
return _lastResult;
}
//No need to wrap the code in a Task.Run. Just await the async code
private static async Task<int> CalculateNextAsync()
await Task.Delay(2000);
return ++_i;
}
//Event Handlers allow for async void
private async void Button_Click(object sender, RoutedEventArgs e) {
while( true) {
var result = await Calculate();
Debug.WriteLine(result.ToString());
}
}
ORIGINAL ANSWER
You are mixing async/await and blocking calls like .Result in the UWP app which is causing a deadlock because of its one chunk SynchronizationContext. Console applications are an exception to that rule, which is why it works there and not in the UWP app.
The root cause of this deadlock is due to the way await handles
contexts. By default, when an incomplete Task is awaited, the current
“context” is captured and used to resume the method when the Task
completes. This “context” is the current SynchronizationContext unless
it’s null, in which case it’s the current TaskScheduler. GUI and
ASP.NET applications have a SynchronizationContext that permits only
one chunk of code to run at a time. When the await completes, it
attempts to execute the remainder of the async method within the
captured context. But that context already has a thread in it, which
is (synchronously) waiting for the async method to complete. They’re
each waiting for the other, causing a deadlock.
Note that console applications don’t cause this deadlock. They have a
thread pool SynchronizationContext instead of a one-chunk-at-a-time
SynchronizationContext, so when the await completes, it schedules the
remainder of the async method on a thread pool thread. The method is
able to complete, which completes its returned task, and there’s no
deadlock. This difference in behavior can be confusing when
programmers write a test console program, observe the partially async
code work as expected, and then move the same code into a GUI or
ASP.NET application, where it deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
Related
I have an application that regularly launches fire-and-forget tasks, mainly for logging purposes, and my problem is that when the application is closed, any currently running fire-and-forget tasks are aborted. I want to prevent this from happening, so I am searching for a mechanism that will allow me to await the completion of all running fire-and-forget operations before closing my app. I don't want to handle their possible exceptions, I don't care about these. I just want to give them the chance to complete (probably with a timeout, but this is not part of the question).
You could argue that this requirement makes my tasks not truly fire-and-forget, and there is some truth in that, so I would like to clarify this point:
The tasks are fire-and-forget locally, because the method that launches them is not interested about their outcome.
The tasks are not fire-and-forget globally, because the application as a whole cares about them.
Here is a minimal demonstration of the problem:
static class Program
{
static async Task Main(string[] args)
{
_ = Log("Starting"); // fire and forget
await Task.Delay(1000); // Simulate the main asynchronous workload
CleanUp();
_ = Log("Finished"); // fire and forget
// Here any pending fire and forget operations should be awaited somehow
}
private static void CleanUp()
{
_ = Log("CleanUp started"); // fire and forget
Thread.Sleep(200); // Simulate some synchronous operation
_ = Log("CleanUp completed"); // fire and forget
}
private static async Task Log(string message)
{
await Task.Delay(100); // Simulate an async I/O operation required for logging
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
}
}
Output:
11:14:11.441 Starting
11:14:12.484 CleanUp started
Press any key to continue . . .
The "CleanUp completed" and "Finished" entries are not logged, because the application terminates prematurely, and the pending tasks are aborted. Is there any way that I can await them to complete before closing?
Btw this question is inspired by a recent question by #SHAFEESPS, that was sadly closed as unclear.
Clarification: The minimal example presented above contains a single type of fire-and-forget operation, the Task Log method. The fire-and-forget operations launched by the real world application are multiple and heterogeneous. Some even return generic tasks like Task<string> or Task<int>.
It is also possible that a fire-and-forget task may fire secondary fire-and-forget tasks, and these should by allowed to start and be awaited too.
One reasonable thing is to have in-memory queue inside your logger (this applies to other similar functionality matching your criterias), which is processed separately. Then your log method becomes just something like:
private static readonly BlockingCollection<string> _queue = new BlockingCollection<string>(new ConcurrentQueue<string>());
public static void Log(string message) {
_queue.Add(message);
}
It's very fast and non-blocking for the caller, and is asynchronous in a sense it's completed some time in the future (or fail). Caller doesn't know or care about the result, so it's a fire-and-forget task.
However, this queue is processed (by inserting log messages into final destination, like file or database) separately, globally, maybe in a separate thread, or via await (and thread pool threads), doesn't matter.
Then before application exit you just need to notify queue processor that no more items are expected, and wait for it to complete. For example:
_queue.CompleteAdding(); // no more items
_processorThread.Join(); // if you used separate thread, otherwise some other synchronization construct.
EDIT: if you want for queue processing to be async - you can use this AsyncCollection (available as nuget package). Then your code becomes:
class Program {
private static Logger _logger;
static async Task Main(string[] args) {
_logger = new Logger();
_logger.Log("Starting"); // fire and forget
await Task.Delay(1000); // Simulate the main asynchronous workload
CleanUp();
_logger.Log("Finished"); // fire and forget
await _logger.Stop();
// Here any pending fire and forget operations should be awaited somehow
}
private static void CleanUp() {
_logger.Log("CleanUp started"); // fire and forget
Thread.Sleep(200); // Simulate some synchronous operation
_logger.Log("CleanUp completed"); // fire and forget
}
}
class Logger {
private readonly AsyncCollection<string> _queue = new AsyncCollection<string>(new ConcurrentQueue<string>());
private readonly Task _processorTask;
public Logger() {
_processorTask = Process();
}
public void Log(string message) {
// synchronous adding, you can also make it async via
// _queue.AddAsync(message); but I see no reason to
_queue.Add(message);
}
public async Task Stop() {
_queue.CompleteAdding();
await _processorTask;
}
private async Task Process() {
while (true) {
string message;
try {
message = await _queue.TakeAsync();
}
catch (InvalidOperationException) {
// throws this exception when collection is empty and CompleteAdding was called
return;
}
await Task.Delay(100);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
}
}
}
Or you can use separate dedicated thread for synchronous processing of items, as usually done.
EDIT 2: here is variation of reference counting which doesn't make any assumptions about nature of "fire and forget" tasks:
static class FireAndForgetTasks {
// start with 1, in non-signaled state
private static readonly CountdownEvent _signal = new CountdownEvent(1);
public static void AsFireAndForget(this Task task) {
// add 1 for each task
_signal.AddCount();
task.ContinueWith(x => {
if (x.Exception != null) {
// do something, task has failed, maybe log
}
// decrement 1 for each task, it cannot reach 0 and become signaled, because initial count was 1
_signal.Signal();
});
}
public static void Wait(TimeSpan? timeout = null) {
// signal once. Now event can reach zero and become signaled, when all pending tasks will finish
_signal.Signal();
// wait on signal
if (timeout != null)
_signal.Wait(timeout.Value);
else
_signal.Wait();
// dispose the signal
_signal.Dispose();
}
}
Your sample becomes:
static class Program {
static async Task Main(string[] args) {
Log("Starting").AsFireAndForget(); // fire and forget
await Task.Delay(1000); // Simulate the main asynchronous workload
CleanUp();
Log("Finished").AsFireAndForget(); // fire and forget
FireAndForgetTasks.Wait();
// Here any pending fire and forget operations should be awaited somehow
}
private static void CleanUp() {
Log("CleanUp started").AsFireAndForget(); // fire and forget
Thread.Sleep(200); // Simulate some synchronous operation
Log("CleanUp completed").AsFireAndForget(); // fire and forget
}
private static async Task Log(string message) {
await Task.Delay(100); // Simulate an async I/O operation required for logging
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
}
}
Perhaps something like a counter to wait on exit? This would still be pretty much fire and forget.
I only moved LogAsync to it's own method as to not need the discard every time Log is called. I suppose it also takes care of the tiny race condition that would occur if Log was called just as the program exited.
public class Program
{
static async Task Main(string[] args)
{
Log("Starting"); // fire and forget
await Task.Delay(1000); // Simulate the main asynchronous workload
CleanUp();
Log("Finished"); // fire and forget
// Here any pending fire and forget operations should be awaited somehow
var spin = new SpinWait();
while (_backgroundTasks > 0)
{
spin.SpinOnce();
}
}
private static void CleanUp()
{
Log("CleanUp started"); // fire and forget
Thread.Sleep(200); // Simulate some synchronous operation
Log("CleanUp completed"); // fire and forget
}
private static int _backgroundTasks;
private static void Log(string message)
{
Interlocked.Increment(ref _backgroundTasks);
_ = LogAsync(message);
}
private static async Task LogAsync(string message)
{
await Task.Delay(100); // Simulate an async I/O operation required for logging
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
Interlocked.Decrement(ref _backgroundTasks);
}
}
I have a situation where a user creates the instance of a class when the user clicks on a tabitem. This class contains a function which plots a graph and it's very time consuming so it is written async.
Now the problem is lets say the user first time click on tab and the class instantiates and the long process works for long time and meanwhile where the previous async task is not finshed and the user clicked again on the same tabitem.
In this situation i must wait until the previous async task is not finished and then on second click to tabitem must create instance after teh fist async task is finshed (It should wait until the first async process is not finshed).
The code is here:
if (selectedTabIndex == 2) //What is the user selected second time whil the previous task is still not finshed ?
{
DrawGraph obj= new DrawGraph(selectedItem.Name);
}
Somewhere in DrawGraph class constructor i have done:
public DrawGraph(string Name)
{
timeConsumingProcess(Name);
}
public async void timeConsumingProcess(string Name)
{
await startTaimeConsumingProcess();
}
What i want is when user clicks the second time this tab item number=2 then it must wait until the previous async task to finsh and then it must instantiate again DrawGraph class to
restart async again.
How to achieve it ?
In this situation i must wait until the previous async task is not finished and then on second click to tabitem must create instance after teh fist async task is finshed
Then have your code (a)wait on the last task:
private Task drawGraph = null;
...
if (selectedTabIndex == 2)
{
if (drawGraph != null)
await drawGraph;
DrawGraph obj = new DrawGraph(selectedItem.Name);
drawGraph = obj.timeConsumingProcess();
}
...
private readonly string name;
public DrawGraph(string Name)
{
name = Name;
}
public async Task timeConsumingProcess()
{
await startTaimeConsumingProcess();
}
Note that this requires you to use async Task instead of async void, which is good anyway because you should avoid async void.
You can store the Task representing the long running action in an instance variable in the form and use this to check whether the task is still running.
private Task drawGraphTask = null;
private void button1_Click(object sender, EventArgs e)
{
DrawGraph();
}
private async void DrawGraph()
{
// Only perform task when this is the first time
// or the previous task is already completed
if (drawGraphTask == null || drawGraphTask.IsCompleted)
{
drawGraphTask = startTimeConsumingProcess();
await drawGraphTask;
}
else
{
MessageBox.Show("Task already active");
}
}
private Task startTimeConsumingProcess()
{
// Your Code here
return Task.Delay(5000);
}
It is not good to start an asynchronous activity from inside a constructor. Move the asynchronous logic outside the DrawGraph class and make instantiating the class asynchronous at the level of the form already.
If it is a very time consuming process, you can choose to do it in a different thread
public DrawGraph(string Name)
{
var task = new Task(() => timeConsumingProcess(Name));
task.Start();
}
This way your main thread won't be blocked. If you want to run some code after this long running task is finished, use the ContinueWith method.
public DrawGraph(string Name)
{
_canPressTab = false;
var task = new Task(() => timeConsumingProcess(Name));
task.Start();
task.ContinueWith(t => {
_canPressTab = true;
...
});
}
UPDATE BELOW
As seen here, you can call the Draw method when you click on a tab. This checks if a task is already running. If not, it starts the task. If the task is running it will wait on it for it to complete and then start a new task.
public Task task = new Task(DoSomething);
private static void DoSomething()
{
Thread.Sleep(10000);
}
private void Draw()
{
//if tab is clicked run the Draw method
if (task.Status.Equals(TaskStatus.Running))
{
task.Wait();
task = new Task(DoSomething);
task.Start();
}
else if (task.Status.Equals(TaskStatus.RanToCompletion))
{
task = new Task(DoSomething);
task.Start();
}
else
{
task.Start();
}
}
A simple change will achieve this
public async Task DrawGraph(string Name)
{
await timeConsumingProcess(Name);
}
public async Task timeConsumingProcess(string Name)
{
await startTaimeConsumingProcess();
}
I made DrawGraph() return a Task as well in case you need to await it.
I'm trying to avoid having to chain a bunch of BackgroundWorkers together. I'm doing something that requires me to wait for the UI to update before continuing execution. Obviously, I can't use Sleep, as this blocks the UI thread from updating and defeats the purpose. I found the code below which I thought was the answer, but it appears the task.Wait(); line is still blocking the UI thread.
static void Main(string[] args)
{
var task = Task.Run(() => DoSomething());
task.Wait();
// once the task completes, now do more
}
static void DoSomething()
{
// something here that is looking for the UI to change
}
I also tried the following, which did the same thing:
static void Main(string[] args)
{
var task = Task.Run(() => DoSomethingAsync());
task.Wait();
// once the task completes, now do more
}
private async Task DoSomethingAsync()
{
// something here that is looking for the UI to change
}
Is it possible to do what I want, and if so, what am I doing wrong?
You need to await the task instead of blocking on it. You can do that inside an async method.
Now, Main can't be async but an event handler can be (which I guess is where you actually use that code):
public async void EventHandler(object sender, EventArgs e)
{
await Task.Run(() => DoSomething()); // wait asynchronously
// continue on the UI thread
}
Note that it's async void which should only be used on event handlers. All other async methods should return a task.
Using Task.Run means your using a ThreadPool thread. To really wait asynchronously for the UI to "do something" you should use TaskCompletionSource. You create it and await it's Task property and you complete that task when the UI changed:
public async void EventHandler(object sender, EventArgs e)
{
_tcs = new TaskCompletionSource<bool>();
await _tcs.Task;
}
public void UIChanged(object sender, EventArgs e)
{
_tcs.SetResult(false);
}
How can i know if an async(awaitable) operation is already running and waiting for completion in the application. I have two buttons in a view each binded to two differrent async methods. If button1 is clicked and the async is started and waiting for its result. And at that time if the button2 is clicked. I need to show a message that an already running async methos is there and stop the second async method from executing. How can i achieve this?
Store the task and check for IsCompleted Property.
private Task pendingTask = Task.FromResult(0);
private async void Button1Click()
{
if (!pendingTask.IsCompleted)
{
//Notify the user
return;
}
pendingTask = DoSomethingAsync();
await pendingTask;
...
}
private async void Button2Click()
{
if (!pendingTask.IsCompleted)
{
//Notify the user
return;
}
pendingTask = DoSomethingElseAsync();
await pendingTask;
...
}
As noted by #Peter Ritchie in comments, better idea is to disable the other button when asynchronous operation is pending. You may consider using it.
Task class has a Status property which can be used to evaluate if an asynchronous operation is running or it has completed or even if it's in faulted state.
You can store the executed Task inside your form and look up its Status property:
public class Form1
{
private Task fooTask = Task.FromResult(0);
public Task FooAsync()
{
return Task.FromResult(0);
}
public async void MyEventHandler(object sender, EventArgs e)
{
if (fooTask.Status == TaskStatus.Running)
{
// If we got here, the task is currently running. Notify someone
return;
}
// If we're here, the task isn't running.
}
}
Note this doesn't take care of situations where your task might be in a Faulted or Canceled state, which you may want to handle as well.
I have a connection class which has several async methods such as SendText, SendImage etc.
The connection class has a Disconnect method, and when it is called I have to be careful not to start changing the inner state of the class before all async methods have completed execution.
I believe a good way to achieve this is to simply keep a running total of the number of operations in execution, and then when I want to Disconnect I can simply set Disconnecting = true and then wait for the count to reach 0
I'm thinking of something along the lines of this
class ReferenceCounter
{
void Increment();
void Decrement();
async Task WaitForCounterToReachZero();
}
Then when an async operation starts I could do
refCounter.Increment();
When it ends
refCounter.Decrement();
and inside the Disconnect method
disconnecting = true;
taskCancellationSource.Cancel();
await refCounter.WaitForCounterToReachZero();
Cleanup();
Are there any built in .NET classes like this?
Or more importantly for me, is there a better way of doing this?
If it was synchronous code it would be as simple as
lock (thisLock)
{
while (counter > 0)
Monitor.Wait(thisLock);
}
I just found the built in CountdownEvent class which does the same thing, but it has no async Wait method nor does it have any events, so I'd have to block.
Well, assuming you'll never increment again after you make it 0, you could do something like this:
public class Latch
{
private int count = 0;
private readonly TaskCompletionSource<object> tcs =
new TaskCompletionSource<object>();
public void Increment()
{
Interlocked.Increment(ref count);
}
public void Decrement()
{
if (Interlocked.Decrement(ref count) == 0)
{
tcs.TrySetValue(null);
}
}
public Task Task { get { return tcs.Task; } }
}
Then you can await someLatch.Task. Alternatively, you could make the latch itself awaitable:
public TaskAwaiter GetAwaiter()
{
return tcs.Task.GetAwaiter();
}
You should probably consider how you want to guard against the "count rises after getting down to 0" aspect thuogh - think about what you'd want it to do. (In the code above, once the TCS's value has been set, further awaits will complete immediately.)