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);
}
}
Related
I have a background task in my software which should run indefinitely.
The code repeating the task (and possibly other tasks in the future) looks like this:
public class RunTicketTasks
{
public async Task RunBackgroundTasks()
{
AssignTickets assignTickets = new AssignTickets();
while (true)
{
await assignTickets.AssignTicketCreator();
await Task.Delay(5 * 60 * 1000);
}
}
}
At the same time I have the WPF UI MainWindow.xaml which is the application entry point as far as I know.
within public MainWindow I start the task like the following way:
public MainWindow()
{
InitializeComponent();
JiraBackgroundTasks.RunTicketTasks ticketTasks = new JiraBackgroundTasks.RunTicketTasks();
ticketTasks.RunBackgroundTasks();
}
Apparently, this starts the task on the same thread (I believe). The task is started and running successfully, but the UI is beeing blocked due to long running operations. Meanwhile, when I uncomment the last line ticketTasks.RunBackgroundTasks(); the UI just runs fine.
How do I start the task in the background so that my User Interface is still responsive?
EDIT:
The reason I started the task this way was because of exception handling.
I can successfully start the task on a different thread as suggested with Task.Run(async () => await ticketTasks.RunBackgroundTasks()); But then I will not receive any exception if something goes wrong.
How can I start the task not blocking the UI and still receive the exception details if an exception is thrown?
The Internet states the following method:
await Task.Run(() => ticketTasks.RunBackgroundTasks());
But this will not work because The await operator can only be used within an async method.
One way to do it is to wrap the task in an async event callback:
public partial class MainWindow : Window
{
// I mocked your missing types, replace with your own
class AssignTickets
{
public Task AssignTicketCreator() => Task.CompletedTask;
}
public class RunTicketTasks
{
public async Task RunBackgroundTasks()
{
AssignTickets assignTickets = new AssignTickets();
int x = 0;
while (true)
{
await assignTickets.AssignTicketCreator();
await Task.Delay(1000);
var isRunningOnDispatcher = Application.Current.Dispatcher ==
System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread);
Trace.WriteLine($"One second passed, running on dispatcher: {isRunningOnDispatcher}");
// exception will also get thrown on the main thread
if (x++ > 3) throw new Exception("Hello!");
}
}
}
public MainWindow()
{
InitializeComponent();
// event fires on UI thread!
this.Loaded += async (sender, args) =>
{
try
{
await new RunTicketTasks().RunBackgroundTasks();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
};
}
}
I have a dispatcher object that handles the starting up and shutting down of tasks as requests come in to my service through a pipe.
My on stop method looks like this:
protected override void OnStop()
{
try
{
Task t = CalculationServiceProcess.Instance.Shutdown();
t.Wait();
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
Debug.WriteLine(e.Message);
}
}
}
My Instance.Shutdown Method is as follows:
public Task Shutdown()
{
_shutdownTokenSource.Cancel();
Task t = _dispatcher.Shutdown();
_shutdownTokenSource.Dispose();
///some more code
return t;
}
And the dispatchers shutdown method looks like:
public Task Shutdown()
{
Task t = Task.WhenAll(_dispatchedItems.Select((item => item.Value.Task)).ToArray());
_dispatchedItems.Clear();
return t;
}
What I am wondering is will the dispose method be called and will the "//some more code" be guaranteed to run? Or when the WhenAll task finishes, will my t.Wait() method return and OnStop finish executing, and the service shut down before that code executes?
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 am working in Xamarin app, but I think my question is more focused in .NET framework and C#.
For example I navigate to PageOne, and in constructor is called the asynchronous method InitializePageOneData()...
public PageOne()
{
await InitializePageOneData()
}
But just in this moment, while it is waiting for the method execution, I navigate to a second page (PageTwo), which has another asynchronous operations, but I see that the InitializePageOneData() method does not stops his execution.
My goal is to stop this asynchronous operation before doing the navigation to another page. What is your recommendation?
Note: in the asynchronous operation, I am using TaskCompletionSource:
private Task<Response> ProcessRequest(Request request)
{
tsc = new TaskCompletionSource<Response>();
eventHandler = (s, e) =>
{
try
{
_client.ProcessRequestsCompleted -= eventHandler;
tsc.TrySetResult(e.Result);
}
catch (Exception ex)
{
_client.ProcessRequestsCompleted -= eventHandler;
tsc.TrySetException(ex);
}
};
_client.ProcessRequestsCompleted += eventHandler;
_client.ProcessRequestsAsync(request);
return tsc.Task;
}
My goal is to stop this asynchronous operation before doing the
navigation to another page. What is your recommendation?
Pass down a CancellationToken to your async method, and monitor on that token. If the user wants to navigate away to page two, use the CancellationTokenSource to cancel that operation.
An example would be:
private CancellationTokenSource cts = new CancellationTokenSource();
public async Task LoadPageOneAsync()
{
try
{
await InitializePageOneDataAsync(cts.Token)
}
catch (OperationCanceledException e)
{
// Handle if needed.
}
}
public async Task InitializePageOneDataAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var something in collection)
{
cancellationToken.ThrowIfCancellationRequested();
// Do other stuff
}
}
When you want to cancel, invoke cts.Cancel() from PageTwo:
public async Task LoadPageTwoAsync()
{
cts.Cancel();
cts = new CancellationTokenSource();
await LoadSecondPageAsync();
}
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.