Why invoking the event cancels the task? - c#

I have a Task that checks the time of a device per second:
public void checkTimeStart()
{
CancellationTokenSource cTokenSource = new CancellationTokenSource();
CancellationToken ct = cTokenSource.Token;
Task task = Task.Factory.StartNew(async () =>
{
ct.ThrowIfCancellationRequested();
while (!ct.IsCancellationRequested)
{
await Task.Delay(1000);
string time = getTime();
TimeUpdateEvent.Invoke(this, new TimeUpdateEventArgs(time));
}
}, cTokenSource.Token);
}
This works perfect if I remove the TimeUpdateEvent.Invoke(this, new TimeUpdateEventArgs(time));. But when I try to invoke the event the task stops completely and it never enters the while loop! I need this event to update time text box whenever I recieve the new time from the device.
I know its possible to update ui directly from anonymous task but this method is implemented in a portable class library. I need it to be platform independent. So every user could update its own ui when they receive TimeUpdateEvent. TimeUpdateEvent is a simple custom event.

Please check if there is any subscription to the event "TimeUpdateEvent" before calling the "checkTimeStart()" method. I think you didn't make any subscription, so on invoking that event system halts. If you will put the invocation part of the code in try catch block:
try
{
TimeUpdateEvent.Invoke(this, new TimeUpdateEventArgs(time));
}
catch (Exception ex)
{
throw;
}
you will get a NullReferenceException ....
So please check the subscription to that event.
Wish you all the best !

Related

Chain tasks using continuation task

I'm trying to chain tasks, so as soon as the one finishes the next starts, but the UI doesn't update. I did a course in react and the one lesson is where you update the UI based on state changes in the application, and that is what I'm trying to replicate. Change the state of the application (basically I'll be running methods that run return a bool for validation), and then update the UI accordingly, I'm also using binding, but for some reason its not running as intended, I don't know if I follow the documentation incorrectly. What can I change or fix to make this work and is it practically correct to use more than one task in a single async Task<T> method
public async Task<string> Connect_To_Ip()
{
await Task.Run(() =>
{
details.State = "Connection To IP 127.0.01.258.....";
Task.Delay(5000).Wait();
}).ContinueWith(result => new Task(async () =>
{
await Task.Run(() =>
{
if (result.Status == TaskStatus.RanToCompletion)
{
details.State = "Validating Card Number......";
}
});
}), TaskContinuationOptions.OnlyOnRanToCompletion);
return details.State;
}
How I'm calling the original task
Task connect = Connect_To_Ip();
await connect;
When you use await then you don't need Task.ContinueWith. Everything that follows the awaited operation is a continuation. Since you want to validate on a background thread, you must post the changes back to the UI thread in order to update the UI elements, otherwise you will produce cross-thread exceptions.
This is because UI elements can't be updated from a background thread, except the update occurs via INotifyPropertyChanged and data binding.
One way to do this is to use the Dispatcher to invoke UI manipulations on the UI thread or use the Progress<T> class, which will always execute the registered callback on the UI thread.
Your fixed and simplified code could look like this example:
public async Task ValidateAsync()
{
// Register the callback that updates the UI with the 'progressReporter'.
// Progress<T> must be instantiated on the UI thread it is associated with
var progressReporter = new Progress<string>(message => details.State = message);
// Execute the operation on a background thread
await Task.Run(() => ConnectToIp(progressReporter));
// Continuation starts here, after await
}
public async Task ConnectToIp(IProgress<string> progressReporter)
{
progressReporter.Report("Connection To IP 127.0.01.258.....");
await Task.Delay(TimeSpan.FromSeconds(5));
// Continuation starts here, after await
progressReporter.Report("Validating Card Number......");
}
It is recommended to use async APIs when possible instead of using background threads. For example, to connect to a server without blocking the UI you can use
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
Many IO classes provide an async API.
Furthermore, I recommend to take a look at the INotifyDataErrorInfo interface. It is the recommended way to implement property validation and allows to provide UI error feedback in a very easy way.
I did this in Windows Forms (I had a test Windows Forms project open), but it should be about the same in WPF. I dropped a button, a label and a text box on the form. Then I wrote this code:
private async void button1_Click(object sender, EventArgs e)
{
var result = await ValidateTextBox();
if (result != null)
{
label1.Text = result;
return;
}
var intResult = await ReadTextBox();
label1.Text = intResult.ToString();
await IncrementTextBox();
intResult = await ReadTextBox();
label1.Text = intResult.ToString();
}
private async Task<string> ValidateTextBox()
{
await Task.Delay(2000);
if (!int.TryParse(textBox1.Text, out _)) {
return "Not Valid";
}
//otherwise
return null;
}
private async Task<int> ReadTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
return result;
}
private async Task IncrementTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
textBox1.Text = (result + 1).ToString();
}
If you type something that's not an int into the text box and press the button, a few seconds go by, and then Not Valid shows up in the label.
If there is a number there, then there is a pause and the number shows up in the label. Then another pause and the text box number will increment by 1. Finally after another pause, the label will show the incremented value.
Note that this all runs on a single thread. But, in spite of all the delays, the UI remains responsive the whole time.
Put breakpoints at the start of each function and on the lines after each of the awaits in the button click handler. Step through (not into) the whole thing and you'll see how the awaits create continuations

How to abort previous execution of an event when it's called again?

I know this is probably one I should be able to google easily enough, but just not sure what to search for at the moment...
Basically, I have an event that is raised and while it is still executing it may be raised again. If this happens, I would like to immediately terminate any previous execution.
In this specific case, I'm talking about a SelectionChanged event for a DataGridView. Before I introduced async programming to this project there was no issue. But now I'm finding that as the method waits for results from an async call, it may be raised again. And the new call may finish before the original call which results in an inconsistent UI state.
How about something like this:
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public async void OnMainThread()
{
cancellationTokenSource.Cancel();
var newCts = new CancellationTokenSource();
cancellationTokenSource = newCts;
try
{
var result = await AsyncOp(newCts.Token);
if (!newCts.IsCancellationRequested)
{
// Update UI
}
}
catch (OperationCanceledException)
{
// Ignore
}
}
public async Task<int> AsyncOp(CancellationToken cancel)
{
// Do the async work
return 0;
}

C# Call async method inside thread

Maybe I did not search correctly here in the forum because I did not find a similar problem.
Well, my problem is when I try to execute an async method inside a thread.
When I run the method (Register) without the thread it works perfectly!
Below is an example of the scenario.
private SyncProcess _sync = new SyncProcess();
private static HttpClient _httpClient = new HttpClient();
private Thread _thread;
public class SyncProcess : ISyncProcess
{
public event CompleteHandler OnComplete = delegate { };
// another properties ...
public void Run()
{
// import rules
// ...
OnComplete();
}
}
public void TestImport()
{
Register(idsync, "start"); // here register works fine
_sync.OnComplete += ImportComplete;
_thread = new Thread(() =>
{
try
{
_sync.Run();
}
catch (Exception ex)
{
// not fall here
}
});
//
_thread.Start();
}
private void ImportComplete()
{
// other end-of-import rules
// ...
Register(idsync, "complete"); // here register not works
}
public async Task<string> Register(int idsync, string type)
{
string url = "myurl";
var stringContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("json", "myjson") });
var response = await _httpClient.PostAsync(url + type, stringContent);
if (response.IsSuccessStatusCode)
{
// do something
}
return "";
}
The problem occurs when I call the method (Register) inside the thread, another thing is that is that it does not generate error does not fall into the try, the debugging simply terminates. I've tried adding try code everywhere but never crashed in catch.
Debug always aborts on the following line:
var response = await _httpClient.PostAsync(url + type, stringContent);
What should I do in this case?
Updated the code returning string in the Register method, but the same error remains.
Thanks any suggestions!
 
Your problem is due to the use of async void, which should be avoided. One of its problems is that you can't catch exceptions using try/catch.
Event handlers in C# are a "fire-and-forget" kind of language feature. In particular, asynchronous event handlers must use async void, and this means the event-publishing code cannot see those exceptions. If you want to allow async event handlers and handle exceptions (or other results) from them, you can use a "deferral" solution or make your event handler delegate return Task instead.
Async void will not allow you to catch any exceptions and will terminate your application when one is thrown. Exceptions are only observed and handled as normal exceptions when using task instead of void.
You can read all about it here link
I can not answer your first question on why it works without the thread without more information. I can guarantee you thought that it has nothing to do with multi threading as far as I know since the main thread is also just a thread like any other.

How Do I Create a Looping Service inside an C# Async/Await application?

I have written a class with a method that runs as a long-running Task in the thread pool. The method is a monitoring service to periodically make a REST request to check on the status of another system. It's just a while() loop with a try()catch() inside so that it can handle its own exceptions and and gracefully continuing if something unexpected happens.
Here's an example:
public void LaunchMonitorThread()
{
Task.Run(() =>
{
while (true)
{
try
{
//Check system status
Thread.Sleep(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
});
}
It works fine, but I want to know if there's another pattern I could use that would allow the Monitor method to run as regular part of a standard Async/Await application, instead of launching it with Task.Run() -- basically I'm trying to avoid fire-and-forget pattern.
So I tried refactoring the code to this:
public async Task LaunchMonitorThread()
{
while (true)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
But when I try to call the method in another async method, I get the fun compiler warning:
"Because this call is not awaited, execution of the current method continues before the call is completed."
Now I think this is correct and what I want. But I have doubts because I'm new to async/await. Is this code going to run the way I expect or is it going to DEADLOCK or do something else fatal?
What you are really looking for is the use of a Timer. Use the one in the System.Threading namespace. There is no need to use Task or any other variation thereof (for the code sample you have shown).
private System.Threading.Timer timer;
void StartTimer()
{
timer = new System.Threading.Timer(TimerExecution, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
}
void TimerExecution(object state)
{
try
{
//Check system status
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
From the documentation
Provides a mechanism for executing a method on a thread pool thread at specified intervals
You could also use System.Timers.Timer but you might not need it. For a comparison between the 2 Timers see also System.Timers.Timer vs System.Threading.Timer.
If you need fire-and-forget operation, it is fine. I'd suggest to improve it with CancellationToken
public async Task LaunchMonitorThread(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000, token);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
besides that, you can use it like
var cancellationToken = new CancellationToken();
var monitorTask = LaunchMonitorThread(cancellationToken);
and save task and/or cancellationToken to interrupt monitor wherever you want
The method Task.Run that you use to fire is perfect to start long-running async functions from a non-async method.
You are right: the forget part is not correct. If for instance your process is going to close, it would be neater if you kindly asked the started thread to finish its task.
The proper way to do this would be to use a CancellationTokenSource. If you order the CancellationTokenSource to Cancel, then all procedures that were started using Tokens from this CancellationTokenSource will stop neatly within reasonable time.
So let's create a class LongRunningTask, that will create a long running Task upon construction and Cancel this task using the CancellationTokenSource upon Dispose().
As both the CancellationTokenSource as the Task implement IDisposable the neat way would be to Dispose these two when the LongRunningTask object is disposed
class LongRunningTask : IDisposable
{
public LongRunningTask(Action<CancellationToken> action)
{ // Starts a Task that will perform the action
this.cancellationTokenSource = new CancellationTokenSource();
this.longRunningTask = Task.Run( () => action (this.cancellationTokenSource.Token));
}
private readonly CancellationTokenSource cancellationTokenSource;
private readonly Task longRunningTask;
private bool isDisposed = false;
public async Task CancelAsync()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
await this.longRunningTask;
}
// for completeness a non-async version:
public void Cancel()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
this.longRunningTask.Wait;
}
}
Add a standard Dispose Pattern
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{ // cancel the task, and wait until task completed:
this.Cancel();
this.IsDisposed = true;
}
}
Usage:
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
The Action {...} has a CancellationToken as input parameter, your function should regularly check it
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
Instead of checking for Token, you could call token.ThrowIfCancellationRequested. This will throw an exception that you'll have to catch

C# How to handle cancel task with eventhandler inside

I have requirement to update ui control when status of dependent service will change. I have this sample code, which polling service api to get status and sends result to recalculate and update ui by main thread:
public void StartObserving() {
this.cts = new CancellationTokenSource();
this.cts.Token.ThrowIfCancellationRequested();
this.isRunning = true;
var token = this.cts.Token;
Task.Run(async () =>
{
try
{
while (this.isRunning)
{
var result = this.serviceAPI.GetStatus();
this.OnServiceStatusChanged(result);
await Task.Delay(3000);
}
}
catch (OperationCanceledException)
{
this.isRunning = false;
}
catch (Exception ex)
{
this.isRunning = false;
this.logger.LogError(ex);
}
}, token);
}
And the problem is when I want to cancel above Task. When I call this.cts.Cancel() in another method in this class, I get Exception 'A task was canceled' on dispatcher which was triggered by EventHandler: OnServiceStatusChanged
How I should properly implement this scenario?
I would simply check whether the token in cancelled in the inner loop, and exit the loop if it is. No need to pass the token to the Task.Run() method.
public void StartObserving()
{
this.cts = new CancellationTokenSource();
var token = this.cts.Token;
Task.Run(async () =>
{
try
{
while (!token.IsCancellationRequested)
{
var result = this.serviceAPI.GetStatus();
this.OnServiceStatusChanged(result);
await Task.Delay(3000);
}
}
catch
{
}
});
}
Tried to simulate this behavior in a console app. Task started, but after calling cts.Cancel(), the task continues to execute... Very strange.
However, I could cancel the task by simply setting this.isRunning to false (instead of calling cts.Cancel()). But I am not sure if this is the solution you want.
If serviceAPI.GetStatus() is a blocking call that waits indeffinitly, then you cannot properly cancel this task.
Proper cancellation of async methods involves marking safe cancellation points with CancellationToken.ThrowIfCancellationRequested().
You would have to rewrite serviceAPI.GetStatus() as an async method that you await the result of. It should contain calls to CancellationToken.ThrowIfCancellationRequested() at points where it can be safely cancelled. You would want to pass the cancellation token in to both that method, and the call to Task.Delay() for optimal performance.

Categories