I'm trying to implement the async-await stuff in a (fairly simple) application.
My goal is to update a busyIndicator in between awaits.
I don't know what, but I think I'm missing somehting essential in my understanding of the async await stuff.
private async void StartTest(object obj)
{
try
{
this.IsBusy = true;
this.BusyMessage = "Init..."
await Task.Delay(7000);
var getData1Task = this.blHandler.GetData1Async();
this.BusyMessage = "Retreiving data...";
this.result1 = await getDeviceInfoTask;
this.result2 = await this.blHandler.GetData2Async();
this.BusyMessage = "Searching...";
this.result3 = await this.blHandler.GetData3();
}
finally
{
this.IsBusy = false;
this.BusyMessage = string.empty;
}
}
The busyIndicator has a binding with IsBusy and BusyMessage.
When executing this code, I do get the busyIndicator showing "Init..." but it never changes to "Retreiving data..." or "Searching...".
Even worse: the ui freezes completely while executing the final GetData3.
Most likely GetData1Async, GetData2Async and GetData3 are synchronous methods (that is, I'm guessing that while they do return a Task that they complete all their work synchronously). In that case, the awaits do not suspend the method (since the returned Task will be a completed task). Thus, the method will continue all the way through as one big synchronous method, and the UI will never have a chance to update (since it is not pumping any messages during this time).
If you want more than a guess, show us the code for those three methods.
It sounds like you actually want to execute a synchronous method on a background thread, then asynchronously wait for it to finish in your UI code.
That's exactly what Task.Run() does.
It takes a delegate to run in the ThreadPool, then returns an awaitable Task that give you the result.
Can you please try pushing message by Dispatcher like.
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{ this.BusyMessage = "Retreiving data..."; }));
// Do Something
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{ this.BusyMessage = "Filtering data..."; }));
// Do Something
Here's a working copy stubbed out with some assumptions on my part. Hope it helps. I tested it in a running WPF app. Note that there is no guarding in what you posted making sure this isn't "double-run" (we can start regardless the value of IsBusy, StartTest should probably ensure that).
public class StackWork : ViewModelBase
{
private class MyHandler
{
private async Task<string> GetDataAsync(string result)
{
return await Task<string>.Run(() =>
{
Thread.Sleep(5000);
return result;
});
}
public async Task<string> GetData1Async()
{
return await GetDataAsync("Data1");
}
public async Task<string> GetData2Async()
{
return await GetDataAsync("Data2");
}
public async Task<string> GetData3()
{
return await GetDataAsync("Data3");
}
}
private bool IsBusy { get; set; }
private string _message = "";
public string BusyMessage
{
get { return _message; }
set { _message = value; RaisePropertyChanged("BusyMessage"); }
}
private MyHandler blHandler = new MyHandler();
private Task<string> getDeviceInfoTask;
private string result1 { get; set; }
private string result2 { get; set; }
private string result3 { get; set; }
public StackWork()
{
getDeviceInfoTask = Task<string>.Run(() =>
{
return ("device info");
});
}
public async void StartTest(object obj)
{
try
{
this.IsBusy = true;
this.BusyMessage = "Init...";
await Task.Delay(7000);
var getData1Task = /*this*/await/*was missing*/ this.blHandler.GetData1Async();
this.BusyMessage = "Retreiving data...";
//assuming this was Task.Run, put that in constructor
this.result1 = getDeviceInfoTask/*this*/.Result/*was missing*/;
this.result2 = await this.blHandler.GetData2Async();
this.BusyMessage = "Searching...";
//This was a little confusing because the name doesn't imply it is
//async, but it is awaited
this.result3 = await this.blHandler.GetData3();
}
finally
{
this.IsBusy = false;
this.BusyMessage = string.Empty;
}
}
}
Related
I working on real-time search. At this moment on property setter which is bounded to edit text, I call a method which calls API and then fills the list with the result it looks like this:
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
RunInAsync(LoadData);
}
}
private async Task LoadData()
{
var response = await connectionRepository.GetConnections(currentPage,
pageSize, searchPhrase);
foreach (UserConnection uc in response)
{
if (uc.Type != UserConnection.TypeEnum.Awaiting)
{
RunOnMainThread(() =>
SearchResult.Add(new ConnectionUser(uc)));
}
}
}
But this way is totally useless because of it totally mashup list of a result if a text is entering quickly. So to prevent this I want to run this method async in a property but if a property is changed again I want to kill the previous Task and star it again. How can I achieve this?
Some informations from this thread:
create a CancellationTokenSource
var ctc = new CancellationTokenSource();
create a method doing the async work
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
// more code here
// check again if this task is canceled
token.ThrowIfCancellationRequested();
// more code
}
}
It is important to have this checks for cancel in the code.
Execute the function:
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
To stop the long running execution use
ctc.Cancel();
For further details please consult the linked thread.
This question can be answered in many different ways. However IMO I would look at creating a class that
Delays itself automatically for X (ms) before performing the seach
Has the ability to be cancelled at any time as the search request changes.
Realistically this will change your code design, and should encapsulate the logic for both 1 & 2 in a separate class.
My initial thoughts are (and none of this is tested and mostly pseudo code).
class ConnectionSearch
{
public ConnectionSearch(string phrase, Action<object> addAction)
{
_searchPhrase = phrase;
_addAction = addAction;
_cancelSource = new CancellationTokenSource();
}
readonly string _searchPhrase = null;
readonly Action<object> _addAction;
readonly CancellationTokenSource _cancelSource;
public void Cancel()
{
_cancelSource?.Cancel();
}
public async void PerformSearch()
{
await Task.Delay(300); //await 300ms between keystrokes
if (_cancelSource.IsCancellationRequested)
return;
//continue your code keep checking for
//loop your dataset
//call _addAction?.Invoke(uc);
}
}
This is basic, really just encapsulates the logic for both points 1 & 2, you will need to adapt the code to do the search.
Next you could change your property to cancel a previous running instance, and then start another instance immediatly after something like below.
ConnectionSearch connectionSearch;
string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
//do your setter work
if(connectionSearch != null)
{
connectionSearch.Cancel();
}
connectionSearch = new ConnectionSearch(value, addConnectionUser);
connectionSearch.PerformSearch();
}
}
void addConnectionUser(object uc)
{
//pperform your add logic..
}
The code is pretty straight forward, however you will see in the setter is simply cancelling an existing request and then creating a new request. You could put some disposal cleanup logic in place but this should get you started.
You can implement some sort of debouncer which will encapsulate the logics of task result debouncing, i.e. it will assure if you run many tasks, then only the latest task result will be used:
public class TaskDebouncer<TResult>
{
public delegate void TaskDebouncerHandler(TResult result, object sender);
public event TaskDebouncerHandler OnCompleted;
public event TaskDebouncerHandler OnDebounced;
private Task _lastTask;
private object _lock = new object();
public void Run(Task<TResult> task)
{
lock (_lock)
{
_lastTask = task;
}
task.ContinueWith(t =>
{
if (t.IsFaulted)
throw t.Exception;
lock (_lock)
{
if (_lastTask == task)
{
OnCompleted?.Invoke(t.Result, this);
}
else
{
OnDebounced?.Invoke(t.Result, this);
}
}
});
}
public async Task WaitLast()
{
await _lastTask;
}
}
Then, you can just do:
private readonly TaskDebouncer<Connections[]> _connectionsDebouncer = new TaskDebouncer<Connections[]>();
public ClassName()
{
_connectionsDebouncer.OnCompleted += OnConnectionUpdate;
}
public void OnConnectionUpdate(Connections[] connections, object sender)
{
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
foreach (var conn in connections)
RunOnMainThread(() => SearchResult.Add(new ConnectionUser(conn)));
}
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
_connectionsDebouncer.Add(RunInAsync(LoadData));
}
}
private async Task<Connection[]> LoadData()
{
return await connectionRepository
.GetConnections(currentPage, pageSize, searchPhrase)
.Where(conn => conn.Type != UserConnection.TypeEnum.Awaiting)
.ToArray();
}
It is not pretty clear what RunInAsync and RunOnMainThread methods are.
I guess, you don't actually need them.
I'm doing the application and I have a problem with the user interface. The user calls up an asynchronous method, which receives the description text. If the user calls the method 10 times, the label in which the description text is displayed is flashed 10 times. It is necessary for me that from 10 times caused method only last showed result, how to me to cancel the previous Tasks (threads)?
private async void DisplayShortDescription(object parameter)
{
var id = (int) parameter; // as string;
var description = await FindDescription(id);
ShortDescription = description.ShortDescription;
CurrentDescriptionId = id;
}
Only last task should execute these lines:
ShortDescription = description.ShortDescription;
CurrentDescriptionId = id;
what you need to do is store the task
so where you have var description = await FindDescription(id);
you can do
task = FindDescription(id);
var description = await task;
where task is
private Task<FindDescriptionReturnType> task;
then you can do something like
public void Button_Click(){
if(task?.IsCompleted??true)
{
DisplayShortDescription()
}
}
however if you do need to cancel the operation the .net way is to use
CancellationToken
CancellationTokenSource source = new CancellationTokenSource();
var task = CancellantionMethod(source.Token);
source.Cancel();
await task;
public async Task CancellantionMethod(CancellationToken token) {
while(!token.IsCancellationRequested)
{
//do something
}
}
most async methods that support cancellation will accept a cancellation token as a parameter
if you can't cancel safely like in the loop above CancellationToken also provides a methodThrowIfCancellationRequested(); which will terminate your code with an exception
Edit: if you really want to check if it is the last thread
If you want to check, then store the task object:
private Task<?> _lastTask;
private async void DisplayShortDescription(object parameter)
{
var id = (int) parameter; // as string;
var currentTask = FindDescription(id)
_lastTask = currentTask;
var description = await currentTask;
if (currentTask == _lastTask)
{
ShortDescription = description.ShortDescription;
CurrentDescriptionId = id;
}
}
What you really want to do is check if the properties have changed:
private async void DisplayShortDescription(object parameter)
{
var id = (int) parameter; // as string;
var description = await FindDescription(id);
if (ShortDescription != description.ShortDescription)
{
ShortDescription = description.ShortDescription;
}
if (CurrentDescriptionId != id)
{
CurrentDescriptionId = id;
}
}
Better still, you can handle this in your property:
private string _shortDescription;
public property string ShortDescription
{
get { return _shortDescription; }
set
{
if (_shortDescription == value)
{
return;
}
_shortDescription = value;
// do other stuff
}
}
I've got this pattern for preventing calling into an async method before it has had a chance to complete previously.
My solution involving needing a flag, and then needing to lock around the flag, feels pretty verbose. Is there a more natural way of achieving this?
public class MyClass
{
private object SyncIsFooRunning = new object();
private bool IsFooRunning { get; set;}
public async Task FooAsync()
{
try
{
lock(SyncIsFooRunning)
{
if(IsFooRunning)
return;
IsFooRunning = true;
}
// Use a semaphore to enforce maximum number of Tasks which are able to run concurrently.
var semaphoreSlim = new SemaphoreSlim(5);
var trackedTasks = new List<Task>();
for(int i = 0; i < 100; i++)
{
await semaphoreSlim.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
// DoTask();
semaphoreSlim.Release();
}));
}
// Using await makes try/catch/finally possible.
await Task.WhenAll(trackedTasks);
}
finally
{
lock(SyncIsFooRunning)
{
IsFooRunning = false;
}
}
}
}
As noted in the comments, you can use Interlocked.CompareExchange() if you prefer:
public class MyClass
{
private int _flag;
public async Task FooAsync()
{
try
{
if (Interlocked.CompareExchange(ref _flag, 1, 0) == 1)
{
return;
}
// do stuff
}
finally
{
Interlocked.Exchange(ref _flag, 0);
}
}
}
That said, I think it's overkill. Nothing wrong with using lock in this type of scenario, especially if you don't expect a lot of contention on the method. What I do think would be better is to wrap the method so that the caller can always await on the result, whether a new asynchronous operation was started or not:
public class MyClass
{
private readonly object _lock = new object();
private Task _task;
public Task FooAsync()
{
lock (_lock)
{
return _task != null ? _task : (_task = FooAsyncImpl());
}
}
public async Task FooAsyncImpl()
{
try
{
// do async stuff
}
finally
{
lock (_lock) _task = null;
}
}
}
Finally, in the comments, you say this:
Seems a bit odd that all the return types are still valid for Task?
Not clear to me what you mean by that. In your method, the only valid return types would be void and Task. If your return statement(s) returned an actual value, you'd have to use Task<T> where T is the type returned by the return statement(s).
I am experiencing some confusion with Tasks and the async/await key words. I understand that you should NOT mix async and blocking code. Or at least what my interpretation of mixing them is:
Don't make calls to blocking API's from non- async methods. So here's my issue.
I am trying to await a method, then update the UI accordingly. The issue is that the only way to await an async method() call is from within and async method().
Here's an example:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
await RunValidation(options, cancellationTokenSource.Token);
Completed = true;
}));
}
}
This code runs the method properly and awaits. The issue is when I return. For some reason when setting the Complete flag the buttons dependent on this flag are not toggled. If I comment the await code, then the buttons are toggled correctly. So assumed I was not returning on the UI thread, so I tried using this code instead:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
var context = TaskScheduler.FromCurrentSynchronizationContext();
await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);
//Completed = true;
}));
}
}
Here is the RunValidation() method:
private async Task RunValidation(Options options, CancellationToken token)
{
await _someService.SomAsyncMethod(options, token));
}
If you notice, the ExecuteCommand has an async key word before the (options) parameter that is passed to the command. If I remove the async key word then I have to modify the call to the RunValidation() method. I still need it to await, so this is what I did:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
{
Completed = false;
var context = TaskScheduler.FromCurrentSynchronizationContext();
var cancellationTokenSource = new CancellationTokenSource();
Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));
Completed = true;
}));
}
}
The problem with this code is that it doesn't await. So I am at a loss.
Can anyone shed some light on this for me please. I've spend 2 plus days on this and I am still here.
Thanks,
Tim
Here are the bindings to the Command Buttons.
private readonly Independent<bool> _completed = new Independent<bool>(true);
public bool Completed
{
get { return _completed; }
set { _completed.Value = value; }
}
private ICommand _doneCommand;
public ICommand DoneCommand
{
get
{
return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
{
DoSomething();
}));
}
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ??
(_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
}
}
I am using the MakeCommand objects from the UpdateControls library from Michael Perry. They contain dependancy tracking that raises the CanExecuteChange events when the Complete property is changed.
Your first code is correct. Most likely you have an incorrect implementation for your Completed property. Your view model object should implement INotifyPropertyChanged. The easiest way to do this right is use a base class that provides the functionality. ReactiveUI is the nuget package I always use. Usage is as simple as
public class MyObject : ReactiveObject {
private bool _Completed;
public bool Completed {
get => _Completed;
set => this.RaiseAndSetIfChanged(ref _Completed, value);
}
}
This will make sure that notifications are raised to the UI when the property is changed.
If you want it to be more magic you can use ReactiveUI.Fody and then your code will reduce to
public class MyObject : ReactiveObject {
[Reactive]
public bool Completed { get; set;}
}
So the issue was in fact the third party library. I was using it to provide dependency tracking for the 2 buttons. So when the complete flag changed it raised the CanExecuteChange events for both buttons without me having write code to do it. Unfortunately it stopped working after introducing the async/await calls. I replaced the 2 MakeCommands with RelayCommands and raised the events myself and everything worked.
So thanks to everyone for your responses.
Tim
In the code below I need to execute the three Get... methods in parallel. When a Get... method completes I need to immediately call the Save... method. Note Save... takes thing as a parameter.
All Get and Save methods must complete before DoStuffAsync returns.
My guess is that I need a continuation on the Get... methods but I dont know how to construct it.
protected async void DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
// We need to run all three Get... methods in parallel
// As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .
var getRed = rep.GetRedAsync().ContinueWith<Task<string>>(async x => { thing.Color1 = x.Result; await rep.SaveRedAsync(thing); return x; }); // does not compile
var getBlue = rep.GetBlueAsync();
var getGreen = rep.GetGreenAsync();
string red = await getRed.Result; // this is not good because getBlue may finish before getRed. We want dont want to wait on getRed before calling SaveBlue
await rep.SaveRedAsync(thing);
var b = await getBlue;
var c = await getGreen;
// thing must be fully initialized before DoStuffAsync returns
}
public class SomeThing
{
public int ID { get; set; }
public string Color1 { get; set; }
public string Color2 { get; set; }
public string Color3 { get; set; }
}
public class SomeRepository
{
public async Task<string> GetRedAsync()
{
return await Task.Run(() => "red");
}
public async Task<string> GetBlueAsync()
{
return await Task.Run(() => "blue");
}
public async Task<string> GetGreenAsync()
{
return await Task.Run(() => "green");
}
public async Task SaveRedAsync(SomeThing thing)
{
// We need thing.ID here as well as other properties
await Task.Delay(1);
}
public async Task SaveBlueAsync(SomeThing thing)
{
await Task.Delay(1);
}
public async Task SaveGreenAsync(SomeThing thing)
{
await Task.Delay(1);
}
}
Well, you could explicitly use ContinueWith - or you could break off each "get and save" into a separate async method or async lambda. For example:
async Task GetAndSaveRedAsync(SomeThing thing, SomeRepository rep)
{
var red = await rep.GetRedAsync();
thing.Red = red;
await SaveRedAsync(red);
// Return red if you want (change the return type to Task<string>)
}
// Ditto for the others
Then:
protected async void DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
var handleRed = GetAndSaveRedAsync(thing, rep);
var handleBlue = GetAndSaveBlueAsync(thing, rep);
var handleYellow = GetAndSaveYellowAsync(thing, rep);
// Or use Task.WhenAll
await handleRed;
await handleBlue;
await handleYellow;
}
I would not mix ContinueWith and await and rather use an async lambda directly:
protected async Task DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
// We need to run all three Get... methods in parallel
// As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .
Func<Task<X>> getRedFunc = async() =>
{
var result = await rep.GetRedAsync();
thing.Color1 = result;
await rep.SaveRedAsync(thing);
return result;
};
var getRed = getRedFunc();
var getBlue = rep.GetBlueAsync();
var getGreen = rep.GetGreenAsync();
await Task.WhenAll(getRed, getBlue, getGreen);
}
Also, don't use async void methods for anything but event handlers. You won't be able to observe the completion of a method like this or handle exceptions possibly thrown inside it.
You can try parallel framework:
using System.Threading;
using System.Threading.Tasks;
Func<Task<string>>[] functions = { rep.GetRedAsync, rep.GetBlueAsync, rep.GetGreenAsync };
Var[] GetArray = new Var[functions.Length]
int i=0;
Parallel.ForEach (var function in functions)
{
GetArray[i++]=function();
}
Note: Require .Net 4