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
}
}
Related
until now I only have small applications, but now I can not handle these classes c# uwp
I want to break the code into several classes, each class will deal with a few parameters. classes will send parameter values to the main page. but when I try to display those parameters, they are always zero, even though the text file appears modified.
i have main page
namespace airflow
{
public sealed partial class MainPage : Page
{
}
public MainPage()
{
}
private async void main_page_Loaded(object sender, RoutedEventArgs e)
{
param_perimetrala read = new param_perimetrala();
ora_start_perimetrala = read.start_perimetrala;
var mesaj = new MessageDialog(ora_start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
}
and a class
namespace airflow
{
public class param_perimetrala
{
public static int ora_start_perimetrala;
public int minut_start_perimetrala;
public int ora_stop_perimetrala;
public int minut_stop_perimetrala;
public int ore_ciclu_perimetrala;
public int minut_ciclu_perimetrala;
public int contor_ore_perimetrala = 0;
public int contor_minute_perimetrala = 0;
public int contor_sec_perimetrala = 0;
public async void readfile_perimetrala()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile perimetrala_file = await folder.CreateFileAsync("parametrii_perimetrala.txt", CreationCollisionOption.OpenIfExists);
var readFile_perimetrala = await FileIO.ReadLinesAsync(perimetrala_file);
int count = 0;
foreach (var line in readFile_perimetrala)
{
string[] split_perimetrala = line.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
var temp = split_perimetrala[1];
if (count == 0)
{
ora_start_perimetrala = Int32.Parse(temp);
}
if (count == 1)
{
minut_start_perimetrala = Int32.Parse(temp);
}
if (count == 2)
{
ora_stop_perimetrala = Int32.Parse(temp);
}
count = count + 1;
}
}
public int start_perimetrala
{
get { return ora_start_perimetrala; }
set { ora_start_perimetrala = value; }
}
}
}
how to send ora_start_perimetrala value in mainpage?
enter image description here
In your main_page_Loaded event handler method you are calling read_file_perimetrala but read_file_perimetrala is async void. This means as soon as you get to an await statement, and the await is actually awaitable (not returned immediately) then the method will put the rest of the work aside until the awaited portion is complete. Then it picks back up and runs the rest. During that time of placing it aside the calling thread then continues to run wherever it's needed.
You need to make this a Task and await the call to is also so that you can insure the parameters are filled before continuing work.
Change your readFile_perimetrala to be like so:
public async Task readfile_perimetralaAsync()
Change the entire main_page_Loaded event handler to read like so...
private async void main_page_Loaded(object sender)
{
param_perimetrala read = new param_perimetrala();
await read.readfile_perimetralaAsync();
var mesaj = new MessageDialog(read.start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
In your code you're assigning the value to a method but I can tell that you want the int value.
Just some light weight examples to help get started understanding Task and async await. Don't use this as a resource but just enough to get you curious to dig deeper because it's a simple but rather deep subject.
public class TaskExamples
{
public async void DoAsyncVoid()
{
await Task.Delay(200);
}
public async Task DoAsyncTask()
{
await Task.Delay(200);
}
public async Task<int> DoReturnValueTask()
{
await Task.Delay(200);
return 50;
}
public async void CallingTasks()
{
DoAsyncVoid(); //This can't use await because it is 'void' so the next line is ran as soon as this command reaches the first 'true awaitable' await.
await DoAsyncTask(); //This runs before DoAsyncVoid is complete.
var value = await DoReturnValueTask(); //This waits until 'DoAsyncTask' is complete because it is a Task and awaited.
await new MessageDialog(value.ToString()).ShowAsync(); //This waits until 'DoReturnValueTask' is complete and value will be 50 in this case.
//All code here waits until the Dialog is closed because it is also awaited.
}
}
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.
Let's say I have a method it gets data from server
Task<Result> GetDataFromServerAsync(...)
If there is an ongoing call in progress, I don't want to start a new request to server but wait for the original to finish.
Let's say I have
var result = await objet.GetDataFromServerAsync(...);
and in a different place, called almost at the same time I have a second call
var result2 = await objet.GetDataFromServerAsync(...);
I don't want the second to start a new request to server if the first didn't finish. I want both calls to get the same result as soon as first call finish. This is a proof of concept, I have options but I wanted to see how easy it's to do this.
Here is a quick example using Lazy<Task<T>>:
var lazyGetDataFromServer = new Lazy<Task<Result>>
(() => objet.GetDataFromServerAsync(...));
var result = await lazyGetDataFromServer.Value;
var result2 = await lazyGetDataFromServer.Value;
It doesn't matter if these 2 awaits are done from separate threads as Lazy<T> is thread-safe, so result2 if ran second will still wait and use the same output from result.
Using the code from here you can wrap this up in a class called AsyncLazy<T>, and add a custom GetAwaiter so that you can just await it without the need to do .Value, very tidy =)
You can use anything for syncrhonization in your method.
For example, I used SemaphoreSlim:
public class PartyMaker
{
private bool _isProcessing;
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private DateTime _something;
public async Task<DateTime> ShakeItAsync()
{
try
{
var needNewRequest = !_isProcessing;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest) return _something;
_isProcessing = true;
_something = await ShakeItSlowlyAsync().ConfigureAwait(false);
return _something;
}
finally
{
_isProcessing = false;
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
Usage:
var maker = new PartyMaker();
var tasks = new[] {maker.ShakeItAsync(), maker.ShakeItAsync()};
Task.WaitAll(tasks);
foreach (var task in tasks)
{
Console.WriteLine(task.Result);
}
Console.WriteLine(maker.ShakeItAsync().Result);
Here is result:
17.01.2017 22:28:39
17.01.2017 22:28:39
17.01.2017 22:28:41
UPD
With this modification you can call async methods with args:
public class PartyMaker
{
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<int, int> _requestCounts = new ConcurrentDictionary<int, int>();
private readonly ConcurrentDictionary<int, DateTime> _cache = new ConcurrentDictionary<int, DateTime>();
public async Task<DateTime> ShakeItAsync(Argument argument)
{
var key = argument.GetHashCode();
DateTime result;
try
{
if (!_requestCounts.ContainsKey(key))
{
_requestCounts[key] = 1;
}
else
{
++_requestCounts[key];
}
var needNewRequest = _requestCounts[key] == 1;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest)
{
_cache.TryGetValue(key, out result);
return result;
}
_cache.TryAdd(key, default(DateTime));
result = await ShakeItSlowlyAsync().ConfigureAwait(false);
_cache[key] = result;
return result;
}
finally
{
_requestCounts[key]--;
if (_requestCounts[key] == 0)
{
int temp;
_requestCounts.TryRemove(key, out temp);
_cache.TryRemove(key, out result);
}
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
public class Argument
{
public Argument(int value)
{
Value = value;
}
public int Value { get; }
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
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
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;
}
}
}