I want to call two or more methods after another. When One function execution get's completed I need to call another method. Basically I am trying to implement the backup functionality in my app. I am developing Windows Phone app which take backup of contacts, images, video. I have created the method for each of these. When backup of contacts is completed then I want to call images method. I have created different functions for these. How can I call these function one after another?
I have tried something like this.
// Constructor
public MainPage()
{
InitializeComponent();
UploadImagesThread = new Thread(UploadImages);
UploadContactsThread = new Thread(UploadContacts);
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
On Upload button click
if (chkContacts.IsChecked.Value)
{
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
});
UploadContactsThread.Start();
}
if (chkImages.IsChecked.Value)
{
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
});
UploadImagesThread.Start();
}
But It will not help me. How do I emplement? My UploadContact method has Async method call like this
Contacts objContacts = new Contacts();
objContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(objContacts_SearchCompleted);
objContacts.SearchAsync(string.Empty, FilterKind.None, null);
Use Task.ContinueWith() to chain up the calls.
You can have a look here: http://msdn.microsoft.com/de-de/library/dd321405(v=vs.110).aspx.
Applied to your problem, this should do the trick:
Task.Factory.StartNew(UploadImages).ContinueWith(UploadContacts);
Try using Tasks, which will let you give you a thread pool for free. You can't do this in a constructor but you could override you OnNavigatedTo Mehtod:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
await Task.Run(() => { UploadImages(); });
await Task.Run(() => { UploadContacts(); });
}
The await will ensure that the contacts will start uploading after your images are completed. You could also check out background uploading which means your app will not have to run while the action is completing.
Your description of the problem (call one method after another?) is not very clear, but looking at your code, I suppose you want to wait for the UploadImagesThread thread to complete before starting the UploadContactsThread.
Use tasks and the async/await keywords like this:
private async void OnButtonClick(object sender, ...)
{
if (chkContacts.IsChecked.Value)
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
await Task.Run(() => UploadImages());
}
if (chkImages.IsChecked.Value)
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
await Task.Run(() => UploadContacts());
}
}
Note: assuming your 2nd block of code is running on the UI thread, you shouldn't need to use BeginInvoke.
Edit
In response to your recent changes: you need a redesign. Try this:
private async void OnButtonClick(object sender, ...)
{
bool uploadContacts = chkContacts.IsChecked.Value;
bool uploadImages = chkImages.IsChecked.Value;
//use this if the continuation runs on the UI thread
Action continuation = async () => {
if(uploadImages) {
SystemTray.ProgressIndicator.Text = "Compressing images...";
await Task.Run(() => UploadImages());
}
};
//OR this if it doesn't
Action continuation = () => {
if(uploadImages) {
Dispatcher.BeginInvoke(() => SystemTray.ProgressIndicator.Text = "Compressing images...");
UploadImages();
}
};
if (uploadContacts)
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
UploadContacts(continuation);
}
}
private void UploadContacts(Action continuation)
{
Contacts objContacts = new Contacts();
//when the search has finished, trigger your event handler AND the continuation task, which will upload the images
objContacts.SearchCompleted += objContacts_SearchCompleted;
objContacts.SearchCompleted += (sender, args) => continuation();
objContacts.SearchAsync(string.Empty, FilterKind.None, null);
}
Try something like that :
bool backContact = chkContacts.IsChecked.Value;
bool backImages = chkImages.IsChecked.Value;
Task.Factory.StartNew(() =>
{
if (backContact) {
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
});
UploadContacts;
});
}).ContinueWith(() =>
{
if (backImages) {
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
});
UploadImages;
}
}
Related
I am trying to wait for the end of a task. This task loads a bunch of things from the server and usually takes about 2 to 3 seconds. This task is started by the command LoadItemsCommand. Once the task is over, it should check if anything has been loaded, and if any of the loaded items already contain values. If yes, it should remove a button from the Toolbar.
But it doesn't wait.
The command call and specifically ask to await the task, but on the main page, things procede without waiting for the end of the task. i tried to put a loop with 30 second wait time to wait for the command to work, but nothing.
Here is the command in the viewmodel:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
Here is the LoadItems Function in the ViewModel
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
List<string> stockIds = new List<string>();
foreach (var orderline in this._order)
{
stockIds.Add(orderline.StockId);
}
string[] array = new string[stockIds.Count];
stockIds.CopyTo(array, 0);
this._products = await Common.GetProducts(new ProductSearchFilter { StockId = array });
_scanStart = Common.Now;
Items.Clear();
bool already_scanned = false; // Todo trouver un meilleur non car pas tres explicite
foreach (PickingOrderLineToHandle orderLine in _order)
{
if (orderLine.ScannedQuantity < orderLine.Quantity)
{
if ((bool)orderLine.WasSentToEnd && !already_scanned)
{
Items.Add(new ListItem() { Name = " ----- ", Id = "-1" });
already_scanned = true;
}
Product product = null;
foreach (var prod in _products)
{
if (prod.StockId == orderLine.StockId)
{
product = prod;
break;
}
}
Items.Add(new ListItem() { Name = $"{(orderLine.Quantity - orderLine.ScannedQuantity).ToString()} / {orderLine.Quantity}\t + {orderLine.Table_bin.LabelAddress} {product.ProductName} {orderLine.stock.Platform}", Id = orderLine.StockId });
}
}
IsBusy = false;
}
And here is the call in the page:
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.LoadItemsCommand.Execute(null);
int loop = 0;
while (viewModel.IsBusy && loop < 60)
{
System.Threading.Thread.Sleep(500);
loop++;
};
if (loop == 60)
{
DisplayAlert("Erreur", "Une erreur est survenue lors du chargment. Veuillez réessayer.", "Ok");
Navigation.PopAsync();
}
var cantCancel = viewModel.Items.Any(i => i.BookedQuantity > 0);
if (Common.IsTeamLeader)
cantCancel = false;
if (cantCancel)
{
var cancelButton = this.ToolbarItems.Where(b => b.Text == "Annuler").First();
this.ToolbarItems.Remove(cancelButton);
}
}
Problem
The following statement seems like a blocking call, but actually, since the method that is called by the Command is an asynchronous anonymous function, it will just execute in a fire and forget fashion and will not be awaited:
viewModel.LoadItemsCommand.Execute(null);
That's because of how the Command is defined:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
A Command like this always executes synchronously. However, when calling an async void method from within a synchronous context, it will just begin execution, but won't await the method (hence fire and forget).
Solution
Now, the way you're using the Command is unusual. Normally, you would bind to the Command from a UI control or you would just trigger its execution. You are expecting it to finish before the rest of the code is executed, which isn't the case as I've explained above.
Instead of executing the Command like that, you could instead do the following:
Make sure ExecuteLoadItemsCommand() is returning a Task
Change the visibility of ExecuteLoadItemsCommand() to public
Change the name to ExecuteLoadItemsAsync() which is a common notation for methods that return asynchronous Tasks
Your new method signature should look something like this:
public async Task ExecuteLoadItemsAsync()
{
//...
}
Then, in your page's code-behind, you could change the OnAppearing() method override to run asynchronously by adding the async keyword to the signature and then awaiting the ExecuteLoadItemsAsync() method:
protected override async void OnAppearing()
{
base.OnAppearing();
await viewModel.ExecuteLoadItemsAsync();
//...
}
This way, you're executing and awaiting the method directly. The Command can still be used for any UI elements to bind to, such as buttons.
Optional improvement
In your ViewModel, you could use an AsyncRelayCommand instead of Command like this:
private AsyncRelayCommand _loadItemsCommand;
public AsyncRelayCommand LoadItemsCommand => _loadItemsCommand ??= new AsyncRelayCommand(ExecuteLoadItemsAsync);
However, this doesn't change the fact that you shouldn't execute the Command in your page's code behind the way you've been attempting.
I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application.
internal void ReadAll()
{
var data = new byte[1];
Task.Factory.StartNew(() =>
{
var ct = new CancellationTokenSource();
var ReadAllTask = Task.Factory.StartNew(() =>
{
// Read all information
// [omit communication exchange via COM port]
ct.Cancel();
}, ct.Token);
// First thread waiting 10s for the worker to finish
ReadAllTask.Wait(10000, ct.Token);
if (ReadAllTask.Status == TaskStatus.RanToCompletion)
{
ReadAllComplete?.Invoke(true);
}
else
{
ct.Cancel();
ReadAllComplete?.Invoke(false);
}
});
}
This method is called by pressing a button. It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown.
Your code could be a lot simpler than current version. Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property.
Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. async/await mechanism will invoke the event in UI context, so code could be something like this:
// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
var data = new byte[2];
// cancel source after 10 seconds
var cts = new CancellationTokenSource(10000);
// Read all information
// [omit communication exchange via COM port]
// async operation with BaseStream
var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);
/*
* if you can't use the BaseStream methods, simply call this method here
* cts.Token.ThrowIfCancellationRequested();
*/
// this code would run only if everything is ok
// check result here in your own way
var boolFlag = result != null;
ReadAllComplete?.Invoke(boolFlag);
}
Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. If at all possible you should switch to a true async API and drop the Task.Run.
private CancellationTokenSource cts;
public async void MyButtonhandler(object sender, EventArgs e) {
cts = new CancellationTokenSource();
try {
var result = await Task.Run(() => ReadAll(cts));
if (result) {
//success
} else {
//failure
}
} catch (TaskCanceledException ex) {
}
}
internal async Task<bool> ReadAll(CancellationTokenSource cts) {
byte[] data = new byte[1];
var timeout = TimeSpan.FromSeconds(10);
var ReadAllTask = Task.Run(() => {
// Read all information
// [omit communication exchange via COM port]
}, cts.Token);
if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
return true;
}
cts.Cancel();
return false;
}
Reading comments and answers to my question I learned a couple of useful things that solve my problem:
CancellationTokenSource can have an implicit timeout
use Task.Run instead Task.Factory.StartNew
don't need to cancel the task, the cts will do the work
Now my code is simpler and it works:
private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
// Start timedout task that will send all necessary commands
CancellationTokenSource cts = new CancellationTokenSource(10000);
Task.Run(() =>
{
oCommandSets.ReadAll(cts);
}, cts.Token);
}
and
internal void ReadAll(CancellationTokenSource cts)
{
// [communication]
if (cts.IsCancellationRequested)
{
ReadAllComplete?.Invoke(false);
}
else
{
ReadAllComplete?.Invoke(true);
}
}
In any case I need to learn more about multithreading.
I'm trying to get data from server and afterwards I need to do few things with that data and other functions.
Because I'm getting data from server I did it with async and continuewith functions.
This is my code:
private void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
Task task = new Task(() => LoginUser().ContinueWith((t) =>
{
afterLogin();
}));
task.Start();
}
}
private void afterLogin()
{
if (result)
{
SharedFunctions.saveDataOnDevice(userID, storeID, permission);
StartActivity(typeof(SplashScreen));
Finish();
}
else
{
SharedFunctions.showHide("Hide", pBar, txt);
SharedFunctions.showPopUp(this, GetString(Resource.String.error_login), GetString(Resource.String.wrong_name_and_password));
}
}
private async Task LoginUser()
{
string userName = uName.Text;
string password = pass.Text;
password = SharedFunctions.encrypt(password);
var client = new RestClient("........");
string resourceStr = #"api/base/....";
var request = new RestRequest(Method.POST)
{
Resource = resourceStr,
RequestFormat = DataFormat.Json
};
request.AddBody(new { UserName = userName, Password = password });
var response = await client.ExecuteTaskAsync<dynamic>(request);
var dt = response.Data;
if (dt != null)
{
userID = dt["ID"];
storeID = dt["StoreID"];
permission = dt["Permission"];
result = true;
}
else
result = false;
}
My main problem is that after I get the data, right after this code: if (dt != null).
When I try to debug the code it reaches the row with userID = dt["ID"]; and even before it is executed it jumps to the afterLogin() function.
What do I need to change in my code to make it run the entire functions before going to the next?
Thank you in advance!
As I describe on my blog, you should never use the Task constructor or the Start method. They are extremely outdated ways to execute code on a thread pool thread, which your app doesn't even need to do since LoginUser is asynchronous. If you did need to execute code on a thread pool thread, the correct API is Task.Run, but in this case you don't need it.
As a side note, you should not use ContinueWith in this situation, either (also explained on my blog). In fact, ContinueWith is downright dangerous; you should use await instead.
After applying these best practices:
private async void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
await LoginUser();
afterLogin();
}
}
When it jumps out that means that the Task breaks (exception) but it doesn't break the whole program as this is an async Task.
Make a breakpoint right threre and check what is inside dt.
Especially with strings like "ID" you often have errors like these. It might be as well "Id" or "id" which is not equal nill but also not matching your "ID".
Good luck!
The problem was very different.
I found out that I cannot convert var that was in the dynamic object to int and that is why the program stopped.
I change this row: userID = dt["ID"]; to this: userID = Convert.ChangeType(dt["ID"], typeof(int));
And now it works.
Thank you all for all of your suggestions.
Task.ContinueWith starts only after completion of the original task.
I'm not sure if this'll help you but try this
private void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
Task.Factory.StartNew(() => { LoginUser(); }).ContinueWith((t) => { afterLogin(); });
}
}
And make your login a normal void function.
I want to run a method on background when app is closed.
async private void()
{
var bgw = new BackgroundWorker();
bgw.DoWork += (s,v) =>
{
string temp= await doSomething();
};
bgw.RunWorkerCompleted += (s,v) =>
{
//...
};
bgw.RunWorkerAsync();
}
It produces the following error
the await operator can only be used within an async lambda expression.
How do I fix it?
DoWork cannot be async. I recommend you use Task.Run instead:
async private void X()
{
await Task.Run(() => doSomething());
//...
}
However, you may need to call Wait instead of await since this is done at application shutdown.
I have to load some object from Azure. I planned to load them five by five in order to take less time (kind of lazy loading) :
ProductManager.LoadProduct call my ProductAccess.LoadProduct
This last method load product from azure, and then raise an event in order that the manager can get the product. Then if it receive product, it call again ProductAccess.LoadProduct.
etc ....
Can't use await/async because it's cross platform code (monodroid/monotouch stable version still don't have await/async).
The first load is correct, then the second call works, but seems my task is not executed (the start do not execute my second task...). I check the thread number and the second time, the Task.Factory.StartNew(() => is executed on the main thread. I try to fix it by specify a long running but still doesn't work.
Here is my code :
Manager side :
public void LoadProduct()
{
ProductAccess.LoadProductsAsync()
}
public void receiveProductsAsync(Object pa, EventArgs e)
{
if (((ProductEventArgs)e).GetAttribute.Equals("LoadProductsAsync"))
{
IoC.Resolve<IProductAccess>().RequestEnded -= receiveProductsAsync;
if ( ((ProductEventArgs)e).LP).Count() != 0)
LoadProductsAsync();
Products = Products.Concat(((ProductEventArgs)e).LP).ToList();
if (((ProductEventArgs)e).E != null)
{
if (RequestEnded != null)
RequestEnded(this, new OperationEventArgs() { Result = false, E = ((ProductEventArgs)e).E, GetAttribute = "LoadProductsAsync" });
}
else
{
if (RequestEnded != null)
{
RequestEnded(this, new OperationEventArgs() { Result = true, GetAttribute = "LoadProductsAsync" });
}
}
}
}
Access side :
public void LoadProductsAsync()
{
Task<ProductEventArgs>.Factory.StartNew(() =>
{
var longRunningTask = new Task<ProductEventArgs>(() =>
{
try
{
var _items = this.table.Select(x => new Product(.....)).Skip(nbrProductLoaded).Take(nbrProductToLoadAtEachTime).ToListAsync().Result;
this.nbrProductLoaded += _items.Count();
Task.Factory.StartNew(() => synchronizeFavorite(_items));
return new ProductEventArgs() { LP = _items, GetAttribute = "LoadProductsAsync" };
}
catch (Exception e)
{
return new ProductEventArgs() { E = e, GetAttribute = "LoadProductsAsync" };
}
}, TaskCreationOptions.LongRunning);
longRunningTask.Start();
if (longRunningTask.Wait(timeout))
return longRunningTask.Result;
return new ProductEventArgs() { E = new Exception("timed out"), GetAttribute = "LoadProductsAsync" };
}, TaskCreationOptions.LongRunning).ContinueWith((x) => {
handleResult(x.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Task.Factory.StartNew will by default use the current TaskScheduler.
The first time, you're not running inside a task, so the current TaskScheduler is the default TaskScheduler (which will run on the thread pool).
When you schedule handleResult back to the original context by using TaskScheduler.FromCurrentSynchronizationContext, that will run handleResult within a task on the main thread. So in that context, the current TaskScheduler is the UI TaskScheduler, not the default TaskScheduler.
To fix this, explicitly pass TaskScheduler.Default to any StartNew that you want to run on a thread pool thread (and remove LongRunning).