How to properly cancel a Task on ViewModel-deactivation with ReactiveUI? - c#

In my MVVM application, when a ViewModel gets activated, a Task gets started that establishes a network connection and could take some time to complete. This Task is cancalable:
private async Task ConnectAsync(CancellationToken cancellationToken = default)
{
...
}
I'm using IActivatableViewModel to start it on ViewModel-activation like that:
// Constructor:
public SomeViewModel(...)
{
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync);
});
}
Now what is the recommended method to cancel this long-running Task when the ViewModel gets deactivated before the Task completes?
I came up with this:
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync).Subscribe().DisposeWith(disposable);
});
Is this the right solution or is there a better one?
Thank you in advance!

Yeah, the code like you show in your code snippet looks good. However, probably worth moving the ConnectAsync method call to a ReactiveCommand<TInput, TOutput> (docs). If you do this, you get such perks as the ability to subscribe to ThrownExceptions and IsExecuting observables, and then display some loading indicators or error messages to keep your users informed about what the app is doing. Also, following the pattern described here, you can cancel that ReactiveCommand<TInput, TOutput> via another command or event. Cancelation via an event would look like this:
// ViewModel.cs
Cancel = ReactiveCommand.Create(() => { });
Connect = ReactiveCommand
.CreateFromObservable(
() => Observable
.StartAsync(ConnectAsync)
.TakeUntil(Cancel));
// View.xaml.cs
this.WhenActivated(disposable => {
this.Events() // Launch the long-running operation
.Loaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Connect)
.DisposeWith(disposable);
this.Events() // Stop that long-running operation
.Unloaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Cancel)
.DisposeWith(disposable);
});
Here, I assume ConnectAsync is a method accepting a cancelation token and returning a Task. In order to enable the this.Events() magic, you need to either use Pharmacist, or to install one of the ReactiveUI.Events packages. But anyway, your option looks good as well if you want to rely on WhenActivated, don't need ThrownExceptions, IsExecuting etc. If you'd like to use commands and rely on WhenActivated, then modify the View.xaml.cs code:
// View.xaml.cs
this.WhenActivated(disposable => {
Connect.Execute().Subscribe();
Disposable
.Create(() => Cancel.Execute().Subscribe())
.DisposeWith(disposable);
});
We aren't disposing of the subscriptions returned by Execute() because they'll get disposed anyway when the commands complete their execution. Hope this helps! ✨

Related

How to start an async method without await its completion?

Sometimes I need to start an async job which works very slow. I don't care if that job success and I need to continue working on my current thread.
Like sometimes I need to send an Email or SMS which works very slow. I need to respond to the web client as soon as possible so I don't want to await it.
I have googled this question and some articles suggest me to write like this:
// This method has to be async
public async Task<Response> SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Run(() => _emailService.SendEmailAsync());
return MyRespond();
}
Or like this:
// This method has to be async
public async Task<Response> SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
}
There will be a warning says: before the call is completed. Consider applying the 'await' operator to the result of the call.
So what if I really awaited it? What is the best practice in C# to 'fire and forget', just call an async method without waiting for its completion?
A standalone discard is the best way to avoid this warning.
_ = Task.Run(() => _emailService.SendEmailAsync());
Discards are dummy variables and can be used to ignore the Task object returned by an asynchronous operation.
https://learn.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
If you truly just want to fire and forget. Simply don't call use await.
// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);
// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);
If you want to use the result output later its gets trickier. But if it is truly fire and forget the above should work
A Cancellation token allows interrupts and canceling procedures. If you are using Cancellation token you will need to use it everywhere from the retrieval straight through to the calling method (Turtles all the way down).
I used ConfigureAwait(false) to prevent deadlocks. Here for more information
EDIT
See the second answer that uses 'Task.Factory.StartNew' I gave this answer some time ago. At the time I didn't realise that the way I did it at the time doesn't ensure completion.
If you need to use async in your function you can also use a discard variable and don't use await. This is also usefull if you have multiple async function calls but you don't need to wait for all of them.
public async function(){
var tmp = await asyncfunction();
...
_ = _httpClient.PutAsync(url, content);
...
}
As Amadan told in the comment that, you need to remove async from your function. then it will stop giving you the warning.
// This method has to be async
public Response SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
}
and Task.Factory.StartNew(() => _emailService.SendEmailAsync()); will indeed work on a new thread.
It all depends on what your Async method accepts. Normally it will accept a "special" class that also holds an event. You can subscribe your callback method to that event and pass it along with the method. When it's finished, your callback method will be called.
An example of this (for sockets) would be:
public void CreateSocket()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
sockAsync.Completed += SockAsync_Completed;
s.ConnectAsync(sockAsync);
}
private void SockAsync_Completed(object sender, SocketAsyncEventArgs e)
{
//Do stuff with your callback object.
}
It all depends on what the method you are trying to call can accept. I would look at the documentation for more help on that specifically.
I am curious why this hasn't been suggested.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
//what ever code here...e.g.
DoSomething();
UpdateSomething();
}).Start();
It just fires off a separate thread.

How to check task status in c#

I'd like to know how can i check status of an asynchronous task in c#.
I have a save method to save users, and i'd like to run a background task to update them after the save.
I'm in framework 4.0, here is my code to begin the task
System.Threading.Tasks.Task task = null;
task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
beginTask();
});
My problem is the task take some times to end (near 7 mins) so if someone do several user saves, the task is runned several times, so i'd like to check before running the task if the function beginTask() is already running to avoid to have a lot of background tasks are running.
Thanks
Maybe try do it this way-
Initialise your task object:
Task task = new Task(begintask);
And add new method to run it, for example:
public void StartTask(Task t)
{
if (t.Status == TaskStatus.Running)
return;
else
t.Start();
}
Of course you can add more conditions depend of task's states.
I think I found better solution. Check if it suits you.
public class TaskDemo
{
private static AutoResetEvent autoReset = new AutoResetEvent(true);
Action beginTask = () =>
{
Console.WriteLine("Method start");
Thread.Sleep(2000);
};
public void RunTask()
{
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
}
}
And simply console app test:
static void Main(string[] args)
{
TaskDemo td = new TaskDemo();
// Simulation multiple requests
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
}
The clue is to use AutoResetEvent to signal task state and
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
to change its state during run and after finish (ContinueWith(t => autoReset.Set()).
Each Task object has a Task.Status property of type TaskStatus which can be queried if you already have the task object. It will tell you whether the task is running, finished, cancelled, etc.
If you want to check if ANY task is running that particular chunk of functionality that may be more difficult. One possible suggestion would be keeping a Task variable globally accessible for that reason and any time they tried to run that functionality the code did:
Check the global to see if one is running.
If not create a new Task to run it and assign it to the global variable
Else perform whatever handling you wanted to do if it was already running (wait for it to finish perhaps?)
It sounds like you're a) misunderstanding tasks a bit and b) need to take a look at your solution design.
A task is an object representing a piece of work to be done. A task is NOT the BeginTask method itself. The BeginTask method is more or less just a set of instructions to carry out. It doesn't have any state. Individual Tasks which implement those instructions do have a state which can be queried.
If you want to make it so only one Task could be run per user you'd just have to somewhere globally store a collection of Tasks per user (such as a Dictionary with the Key being the user).
This would ideally be created and stored in either some sort of governing class that contains this section of application functionality or in the outer program if it is one.
To make reference to your comment of "i need to avoid to create a new task everytime i save my users", for this you're going to have to adapt that particular piece of code to check the stored status of any running Tasks. So in my idea above you'd alter that piece of functionality to check if a Task exists for the user you're saving in the Dictionary of already begun tasks and if it does, check the status of it.
If you're not sure though please keep asking questions in the comments. Perhaps if you gave more information on how the system this is in, is structured I'd be better able to assist.
Hope that helps.
You can use property Task.IsCompleted or Task.Status
And by the way try to investigation of this method Task.ContinueWith
I think using of Task.ContinueWith is better way for multitask solutions.
See example below which explain my suggestion with using Task.ContinueWith:
System.Threading.Tasks.Task task = null;
if (task==null)
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
return;
}
if (task.Status == TaskStatus.Running)
{
task.ContinueWith((x) =>
{
beginTask();
});
}
else
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
}
This implementation can resolve scope of potential problem like:
How to keep previous Task
When should system launch previous Tasks

Why is this Observable blocking the WPF GUI thread (C#)?

Given: An extension method taking a Selenium IWebdriver instance and returning an IObservable
public static IObservable<ObservableCollection<WebElementWrapper>>
GetAllElementsAsObservable(this IWebDriver wd)
{
return Observable.Create<ObservableCollection<WebElementWrapper>>(
(IObserver<ObservableCollection<WebElementWrapper>> observer) =>
{
var eles = wd.FindElements(By.CssSelector("*"));
var list = eles.ToWebElementObservableCollection();
observer.OnNext(list);
observer.OnCompleted();
return Disposable.Create(() => { });
});
}
And the code that calls the method above (running on GUI thread)...
//GUI Will Freeze on this call until OnCompleted is called
cd.GetAllElementsAsObservable().Subscribe((WEWList) =>
{
WebElementCollection = WEWList;
SetNavigationItems();
});
Can anyone help me determine root cause of GUI thread block until OnCompleted is called. I can stop the blocking if I use Task.Run in first method, but then I have to marshall the collection back onto GUI thread.
Does this block because the GUI thread spun up the Webdriver of which the Observable is using to extract elements?
Or is this due to the static methods being created at start up time on the GUI thread?
If ever you do this - Disposable.Create(() => { }) - you are doing something wrong. Using Observable.Create the way you are using it is a blocking operation. The code inside the .Create is part of the subscription, but you're running the observer to completion during subscription which is why it is blocking.
Try doing something like this instead:
public static IObservable<ObservableCollection<WebElementWrapper>>
GetAllElementsAsObservable(this IWebDriver wd)
{
return Observable.Create<ObservableCollection<WebElementWrapper>>(observer =>
Observable
.Start(() =>
wd
.FindElements(By.CssSelector("*"))
.ToWebElementObservableCollection())
.Subscribe(observer));
}
For WPF, I've also found these two methods to work..
SomeObservable
.SubscribeOn(Scheduler.Default)
.ObserveOn(Scheduler.CurrentThread)
.Subscribe(item => { //do something on gui thread here });
I don't care for the method name SubscribeOn, but I look at it this way... I want the observable to SubscribeOn some scheduler. (I think a better name would have been "SheduleOn").
The ObserveOn method name makes sense. But note the "Scheduler.Dispatcher" built-in property.

Wrap method with callback delegate inside Task [duplicate]

I'm writing a networked application.
Messages are sent over the transport as such:
Network.SendMessage (new FirstMessage() );
I can register an event handler to be called when this message type arrives, like so:
Network.RegisterMessageHandler<FirstMessage> (OnFirstMessageReceived);
And the event gets fired:
public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e)
{
}
I'm writing a custom authentication procedure for my networked application, which requires around five messages to complete.
Without using the Task Parallel Library, I would be forced to code the next step of each procedure in the preceding event handler, like so:
public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e)
{
Network.SendMessage( new SecondMessage() );
}
public void OnSecondMessageReceived(EventArgs<SecondMessageEventArgs> e)
{
Network.SendMessage( new ThirdMessage() );
}
public void OnThirdMessageReceived(EventArgs<ThirdMessageEventArgs> e)
{
Network.SendMessage( new FourthMessage() );
}
public void OnFourthMessageReceived(EventArgs<FourthMessageEventArgs> e)
{
// Authentication is complete
}
I don't like the idea of jumping around the source code to code a portion of this and a portion of that. It's hard to understand and edit.
I hear the Task Parallel Library substantially simplifies this solution.
However, many of the examples I read using the Task Parallel Library were related to starting a chain of active tasks. What I mean by 'active', is that each task could start when called explicitly, like so:
public void Drink() {}
public void Eat() {}
public void Sleep() {}
Task.Factory.StartNew( () => Drink() )
.ContinueWith( () => Eat() )
.ContinueWith( () => Sleep() );
This is opposite from my event-based async pattern, in which each event handler method is called only when the message is received.
In other words, I can't do something like this (but I want to):
Task.Factory.StartNew( () => OnFirstMessageReceived() )
.ContinueWith( () => OnSecondMessageReceived() )
.ContinueWith( () => OnThirdMessageReceived() )
.ContinueWith( () => OnFourthMessageReceived() );
I've read this article, but I don't quite understand it. It seems like what I need has to do with TaskCompletionSource. If I wanted to make a task from my event-based async pattern like the code block above, what would it look like?
You're right about TaskCompletionSource, it's the key to transforming EAP (event-based asynchronous pattern) to TPL's Task.
This is documented here: https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/tpl-and-traditional-async-programming#exposing-complex-eap-operations-as-tasks
Here is the simplified code:
public static class Extensions
{
public static Task<XDocument> GetRssDownloadTask(
this WebClient client, Uri rssFeedUri)
{
// task completion source is an object, which has some state.
// it gives out the task, which completes, when state turns "completed"
// or else it could be canceled or throw an exception
var tcs = new TaskCompletionSource<XDocument>();
// now we subscribe to completed event. depending on event result
// we set TaskCompletionSource state completed, canceled, or error
client.DownloadStringCompleted += (sender, e) =>
{
if(e.Cancelled)
{
tcs.SetCanceled();
}
else if(null != e.Error)
{
tcs.SetException(e.Error);
}
else
{
tcs.SetResult(XDocument.Parse(e.Result));
}
};
// now we start asyncronous operation
client.DownloadStringAsync(rssFeedUri);
// and return the underlying task immediately
return tcs.Task;
}
}
Now, all you need to do, to make a chain of those operations, is just to set your continuations (which is not very comfortable at the moment, and the C# 5 await and async will help alot with it)
So, this code could be used like this:
public static void Main()
{
var client = new WebClient();
client.GetRssDownloadTask(
new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"))
.ContinueWith( t => {
ShowXmlInMyUI(t.Result); // show first result somewhere
// start a new task here if you want a chain sequence
});
// or start it here if you want to get some rss feeds simultaneously
// if we had await now, we would add
// async keyword to Main method defenition and then
XDocument feedEric = await client.GetRssDownloadTask(
new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"));
XDocument feedJon = await client.GetRssDownloadTask(
new Uri("http://feeds.feedburner.com/JonSkeetCodingBlog?format=xml"));
// it's chaining - one task starts executing after
// another, but it is still asynchronous
}
Jeremy Likness has a blog entry title Coroutines for Asynchronous Sequential Workflows using Reactive Extensions (Rx) that might interest you. Here is the question he tries to answer:
The concept is straightforward: there are often times we want an asynchronous set of operations to perform sequentially. Perhaps you must load a list from a service, then load the selected item, then trigger an animation. This can be done either by chaining the completed events or nesting lambda expressions, but is there a cleaner way?

c# simple background thread

I am looking for a simple way to do a task in background, then update something (on the main thread) when it completes. It's in a low level 'model' class so I can't call InvokeOnMainThread as I don't have an NSObject around. I have this method:
public void GetItemsAsync(Action<Item[]> handleResult)
{
Item[] items=null;
Task task=Task.Factory.StartNew(() =>
{
items=this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(items);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
This seems to work OK, but
1) Is this the best (simplest) way?
2) I'm worried about the local variable:
Item{} items=null
What stops that disappearing when the method returns before the background thread completes?
Thanks.
I think your method slightly violates a Single Responsibility Principle, because it doing too much.
First of all I suggest to change CreateItems to return Task instead of wrapping it in the GetItemsAsync:
public Task<Item[]> CreateItems(CancellationToken token)
{
return Task.Factory.StartNew(() =>
// obtaining the data...
{});
}
CancellationToken is optional but can help you if you able to cancel this long running operation.
With this method you can remove GetItemsAsync entirely because its so simple to handle results by your client without passing this delegate:
// Somewhere in the client of your class
var task = yourClass.CreateItems(token);
task.ContinueWith(t =>
// Code of the delegate that previously
// passed to GetItemsAsync method
{}, TaskScheduler.FromCurrentSynchronizationContext());
Using this approach you'll get more clear code with only one responsibility. Task class itself is a perfect tool for representing asynchronous operation as a first class object. Using proposed technique you can easily mock you current implementation with a fake behavior for unit testing without changing your clients code.
Something like this:
public void GetItemsAsync(Action<Item[]> handleResult)
{
int Id = 11;
Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two
task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
Your code looks fine.
It's a perfect example of when to use async / await if you can use C# 5, but if not, you have to write it as you have done with continuations.
The items variable is captured in your lambdas, so that's fine too.
When you write a lambda that uses an outside variable, the C# compiler creates a class that contains the variable. This is called a closure and means you can access the variable inside your lambda.
Here is a nicer soultion:
public void GetItemsAsync(Action<Item[]> handleResult)
{
var task = Task.Factory.StartNew<Item[]>(() =>
{
return this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}

Categories

Resources