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.
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 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
When I call BuildCustomer.StartTask, I then call a method WriteToDatabase. Inside WriteToDatabase, I want to send a status back to the MainForm to write the status to the GUI. When the code reaches that point, my application freezes up and gives no error. I did find out that if I remove task.Wait(), it stops freezing and works. But I think I want the wait in because my BuildCustomer takes a bit of time and writes a lot of updates (including more updates from Common class) to the GUI. Can someone tell me what is wrong or what I should be doing differently? This is a .Net 4 project so I cannot use async, which I've seen other answers for.
public partial class MainForm : Window
{
public MainForm()
{
Common.SendMessage += UpdateStatus;
}
private void Button_Click(object sender, EventArgs e)
{
BuildCustomer.StartTask();
}
private void UpdateStatus(string message)
{
Dispatcher.Invoke(new Action(() =>
{
StatusTextBox.Text = message;
}));
}
}
public class BuildCustomer
{
public static void StartTask()
{
var action = new Action<object>(BuildCustomer);
var task = new Task(() => action(buildDetails));
task.Start();
task.Wait();
}
private void BuildCustomerDetails(object buildDetails)
{
Common.WriteToDatabase();
}
}
public class Common
{
public delegate void MessageLogDelegate(string message);
public static event MessageLogDelegate SendMessage;
public static void WriteToDatabase()
{
SendMessage("Some status message to write back to the GUI");
}
}
You have a deadlock. The StartTask waits on task.Wait() to complete but this occurs (is called on) on the calling thread which is the main UI thread.
The Task being waited eventually reaches UpdateStatus which calls an Invoke on the UI thread as well but this thread is currently waiting on task.Wait() (so it is blocking which results in the UI thread not being available indefinitely).
Try to add async keyword to method signature and use this:
await task;
It cause to does not sleep main thread(UI thread).
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.
Here is a simple async call with cancellation code snippet. The code sits in a WPF application class. If I call the Cancel method via a WPF UI Command, the async method will exit properly. However, if the Cancel is invoked during the OnExit method, nothing happens. My actual code requires OnExit call because the async method uses IO resources that should be cleaned up properly.
Any ideas?
Edit: the expected behavior is that Task.Delay method should throw the OperationCancelledException when cancel is invoked. What I want to know is why it doesn't during app exit and if there are work around to get it behaving properly.
public partial class App : Application {
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
ListenAsync(source.Token);
}
ManualResetEvent waitHandle = new ManualResetEvent(false);
CancellationTokenSource source = new CancellationTokenSource();
public void Cancel() {
source.Cancel();
}
async void ListenAsync(CancellationToken token) {
try {
while (true) {
await Task.Delay(300000, token);
}
} catch (OperationCanceledException) {
Console.WriteLine("Cancelled");
} catch (Exception err) {
Console.WriteLine(err.Message);
} finally {
Console.WriteLine("Terminate");
waitHandle.Set();
}
}
protected override void OnExit(ExitEventArgs e) {
Cancel();
waitHandle.WaitOne();
base.OnExit(e);
}
}
Found the problem.
The Cancel call during WPF App Exit is on the same synchronization context as the ListenAsync function. Because the thread is blocked by the waitHandle.WaitOne, there is no way for the ListenAsync method to resume executing on the same synchronization context.
The problem can be resolved by changing the async call to
await Task.Delay(300000, token).ConfigureAwait(false);
This allows the remainder of the ListenAsync function to stay on the sync context of the Task.Delay function.
Not sure if this would solve the issue at hand but per your posted code context , Well probably you can use IsCancellationRequested property of the token object and check that property for the cancellation request and break out of your listen loop like
async void ListenAsync(CancellationToken token) {
try {
while (true)
{
if(token.IsCancellationRequested)
break;
await Task.Delay(300000, token);
}
}