I have the following code and I want that it is executed in the correct order, but I'm not sure if I use "await" correctly so that it is executed in the correct order.
The correct sequence should be:
1)Call GetTiltleData and get the CurrentCatalogVersion.
2)Call GetCatalogData to get the ItemID of the item that will get purchased.
3)Call MakePurchase to purchase the item.
4)Call GetInventoryList to get the player's current(after the purchase) inventory.
Am I using await correctly? Is my code executed in the correct order or is it possible that the code could be executed in a wrong order?
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
GetTiltleData();
GetCatalogData();
public async void GetTiltleData()
{
await ClientGetTitleData();
}
private async Task ClientGetTitleData()
{
var result = await PlayFabClientAPI.GetTitleDataAsync(new GetTitleDataRequest());
if (result.Result.Data == null || !result.Result.Data.ContainsKey("Catalogversion"))
Console.WriteLine(result.Error.GenerateErrorReport());
else
CurrentCatalogVersion = result.Result.Data["Catalogversion"];
}
public async void GetCatalogData()
{
await GetCatalog();
}
private async Task GetCatalog()
{
var result = await PlayFabClientAPI.GetCatalogItemsAsync(new GetCatalogItemsRequest()
{
CatalogVersion = CurrentCatalogVersion
});
foreach (var entry in result.Result.Catalog)
{
//For example, if you want to purchase a sword
if (entry.DisplayName == "Sword")
ItemID = entry.ItemId;
}
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Listed items successful!");
await MakePurchase(ItemID);
}
}
private async Task MakePurchase(string itemid)
{
var result = await PlayFabClientAPI.PurchaseItemAsync(new PurchaseItemRequest()
{
CatalogVersion = CurrentCatalogVersion,
ItemId = ItemID,
Price = 100,
VirtualCurrency = "GO"
});
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Purchase successful!");
await GetInventoryList();
}
}
private async Task GetInventoryList()
{
var result = await PlayFabClientAPI.GetUserInventoryAsync(new GetUserInventoryRequest());
//Get the current amount of the player's virtual currency after the purchase
CurrentAmount = result.Result.VirtualCurrency["GO"];
foreach (var entry in result.Result.Inventory)
{
//Get a list with the player's items after the purchase
Console.WriteLine($"{entry.DisplayName} {entry.UnitPrice} {entry.ItemId} {entry.ItemInstanceId}");
...
}
if (result.Error != null)
{
// Handle error if any
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Got current inventory");
}
}
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
Yes, absolutely.
This is your calling code:
...
GetTiltleData();
GetCatalogData();
These are asynchronous methods, but you're not awaiting their results before continuing to the next instruction. This means both methods are fire and forget threads.
This means you have a race condition. Literally, you have 2 threads racing to finish their methods before the other. You have no guarantee which one will finish first, or how the CPU will prioritise their instructions. Because of how you've called those methods, you've essentially told the computer you don't care about the outcome of events.
Fastest way to ensure one method completes before the next one starts is to await them both. As Igor says, if you are going to use async methods, you should use async/await the entire way up and down your call stack.
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
await GetTiltleData(); // adding "await" to these 2 lines
await GetCatalogData();
In addition, PLEASE don't use async void in your method signatures. You should use async Task instead. See async/await - when to return a Task vs void?.
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'm coding a function that receives a condition to be met and a time out and it finishes when the condition is met or it times out.
This is what I have so far :
public static bool CheckWithTimeout(bool toBeChecked, int msToWait)
{
//var src = CancellationSource
var task = Task.Run(()=> {
while (!toBeChecked)
{
System.Threading.Thread.Sleep(25);
}
});
if (task.Wait(TimeSpan.FromMilliseconds(msToWait)))
return toBeChecked;
else
return false;
}
It works nice for simple bools but I would like to call it like:
CheckWithTimeout(myValue > 10, 500)
And it would return when myValue is bigger than ten, or 500 ms passed (and returns false in this case)
I checked and I think Func is what I need but.. I cannot find a proper example.
Additionally, if there is an already existing method to achieve this I'd definitely prefer it.
It's better to use separate tasks for the result and the waiting.
private async Task<bool> CheckWithTimeout(Func<bool> toBeChecked, int msToWait)
{
var waitTask = Task.Delay(msToWait);
var checkTask = Task.Run(toBeChecked);
await Task.WhenAny(waitTask, checkTask);
return checkTask.IsCompleted && await checkTask;
}
private async Task<bool> CheckWithTimeout<T>(Predicate<T> toBeChecked, T predicateParameter, int msToWait)
{
var waitTask = Task.Delay(msToWait);
var checkTask = Task.Run(() => toBeChecked(predicateParameter));
await Task.WhenAny(waitTask, checkTask);
return checkTask.IsCompleted && await checkTask;
}
That way you do not nescessary have to wait for the timeout.
(And Taks.Delay is better than Task.Wait because it doesn't block)
Edit: Example with function or predicate
If you use a simple bool parameter, it will be evaluated only once when the method is called. If you want to perform the evaluation multiple times, you need to use Func<bool>:
public static bool CheckWithTimeout(Func<bool> toBeChecked, int msToWait)
{
//var src = CancellationSource
var task = Task.Run(()=> {
while (!toBeChecked())
{
System.Threading.Thread.Sleep(25);
}
});
if (task.Wait(TimeSpan.FromMilliseconds(msToWait)))
return toBeChecked();
else
return false;
}
Now you can use a lambda to call the method:
CheckWithTimeout(() => myValue > 10, 500)
Or just create a method which returns a bool and pass its name in.
I am trying to understand async programming, and I had a question. It is regarding the following functions below.
public async void TestAsyncCall() {
Task<string> TaskResult1 = DoSomethingAsync();
string Result2 = DoSomething();
string Result1 = await TaskResult1;
}
public string DoSomething() {
return "synch";
}
public async Task<string> DoSomethingAsync() {
await Task.Delay(10000);
return "asynch";
}
In the function call TestAsyncCall(), would one thread be used to execute DoSomethingAsync(), and another thread to execute DoSomething()?
Then when await is encountered, it would wait for DoSomethingAsync() to complete and release that thread (while also not blocking the original thread)?
Or will this not warrant any new threads being created? In that case will the DoSomethingAsync call be relevant only if it were to deal with some external resource?
I recommend you read my article on async ASP.NET.
Or will this not warrant any new threads being created?
This won't create any new threads. In particular, async and await by themselves won't create any new threads.
On ASP.NET, it's likely that the code after an await will run on a different thread than the code before that await. This is just exchanging one thread for another, though; no new threads are created.
In that case will the DoSomethingAsync call be relevant only if it were to deal with some external resource?
The primary use case for async is to deal with I/O, yes. This is particularly true on ASP.NET.
As #Stepehen-cleary said, "In particular, async and await by themselves won't create any new threads."
This next example is taken from the book: "C sharp in Depth" by John Skeet, chapter 15 pp.465:
class AsyncForm : Form
{
/* The first part of listing 15.1 simply creates the UI and hooks up an event handler for
the button in a straightforward way */
Label label;
Button button;
public AsyncForm()
{
label = new Label {
Location = new Point(10, 20),
Text = "Length"
};
button = new Button {
Location = new Point(10, 50),
Text = "Click"
};
button.Click += DisplayWebSiteLength;
AutoSize = true;
Controls.Add(label);
Controls.Add(button);
}
/* When you click on the button, the text of the book’s home page is fetched
and the label is updated to display the HTML lenght in characters */
async void DisplayWebSiteLength(object sender, EventArgs e)
{
label.Text = "Fetching...";
using (HttpClient client = new HttpClient())
{
string text =
await client.GetStringAsync("http://csharpindepth.com");
label.Text = text.Length.ToString();
}
}
/* The label is updated to display the HTML length in characters D. The
HttpClient is also disposed appropriately, whether the operation succeeds or fails—
something that would be all too easy to forget if you were writing similar asynchronous
code in C# 4 */
}
With this in mind, let's take a look to your code, you have Result1 and Result2, there's no point in having one asynchronous task waiting for a synchronous task to be finished. I would use Parallelism so you can perform both methods but to return something like two sets of Data, performing LINQ queries at the same time.
Take a look to this short example about Parallelism with Async Tasks:
public class StudentDocs
{
//some code over here
string sResult = ProcessDocs().Result;
//If string sResult is not empty there was an error
if (!sResult.Equals(string.Empty))
throw new Exception(sResult);
//some code over there
##region Methods
public async Task<string> ProcessDocs()
{
string sResult = string.Empty;
try
{
var taskStuDocs = GetStudentDocumentsAsync(item.NroCliente);
var taskStuClasses = GetStudentSemesterClassesAsync(item.NroCliente, vencimientoParaProductos);
//We Wait for BOTH TASKS to be accomplished...
await Task.WhenAll(taskStuDocs, taskStuClasses);
//Get the IList<Class>
var docsStudent = taskStuDocs.Result;
var docsCourses = taskStuClasses.Result;
/*
You can do something with this data ... here
*/
}
catch (Exception ex)
{
sResult = ex.Message;
Loggerdb.LogInfo("ERROR:" + ex.Message);
}
}
public async Task<IList<classA>> GetStudentDocumentsAsync(long studentId)
{
return await Task.Run(() => GetStudentDocuments(studentId)).ConfigureAwait(false);
}
public async Task<IList<classB>> GetStudentSemesterCoursessAsync(long studentId)
{
return await Task.Run(() => GetStudentSemesterCourses(studentId)).ConfigureAwait(false);
}
//Performs task to bring Student Documents
public IList<ClassA> GetStudentDocuments(long studentId)
{
IList<ClassA> studentDocs = new List<ClassA>();
//Let's execute a Stored Procedured map on Entity Framework
using (ctxUniversityData oQuery = new ctxUniversityData())
{
//Since both TASKS are running at the same time we use AsParallel for performing parallels LINQ queries
foreach (var item in oQuery.GetStudentGrades(Convert.ToDecimal(studentId)).AsParallel())
{
//These are every element of IList
studentDocs.Add(new ClassA(
(int)(item.studentId ?? 0),
item.studentName,
item.studentLastName,
Convert.ToInt64(item.studentAge),
item.studentProfile,
item.studentRecord
));
}
}
return studentDocs;
}
//Performs task to bring Student Courses per Semester
public IList<ClassB> GetStudentSemesterCourses(long studentId)
{
IList<ClassB> studentCourses = new List<ClassB>();
//Let's execute a Stored Procedured map on Entity Framework
using (ctxUniversityData oQuery = new ctxUniversityData())
{
//Since both TASKS are running at the same time we use AsParallel for performing parallels LINQ queries
foreach (var item in oQuery.GetStudentCourses(Convert.ToDecimal(studentId)).AsParallel())
{
//These are every element of IList
studentCourses.Add(new ClassB(
(int)(item.studentId ?? 0),
item.studentName,
item.studentLastName,
item.carreerName,
item.semesterNumber,
Convert.ToInt64(item.Year),
item.course ,
item.professorName
));
}
}
return studentCourses;
}
#endregion
}
I want to execute a list of tasks, and perform a synchronous action once any of them is completed, but I need to know which of them it was.
See my example, and look out for the comment in the code, that precedes a couple of lines I don't know how to achieve.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
public async Task GreetAllAsync()
{
var tasks = UserNames.Select(un => GreetAsync(un)).ToList();
while(tasks.Any())
{
var finished = await Task.WhenAny(tasks);
if(finished.Result)
{
//Here's what I'd like to achieve
var username = finished.Arguments[0];
WriteLine($"User {username} has been greeted.");
}
tasks.Remove(finished);
}
}
Based on this example.
In my real world scenario, I have a list of customers, which I have to walk thru them one by one and update a remote server on their credit status (the remote server doesn't support batch updates). After each of them has been updated, I have to mark in my database, that this customer has been accredited.
You almost never want to actually process a list of tasks one at a time as they complete like that. Instead, just introduce a higher-level operation and rewrite your Task.WhenAny to a Task.WhenAll to wait for those higher-level operations.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
private async Task<bool> GreetAndReportGreetedAsync(string name)
{
var result = await GreetAsync(name);
WriteLine($"User {name} has been greeted.");
return result;
}
public async Task GreetAllAsync()
{
await Task.WhenAll(UserNames.Select(un => GreetAsync(un));
}
Why not simply use ContinueWith? Something like this:
public async Task GreetAllAsync(List<string> UserNames)
{
var tasks = UserNames
.Select(un => GreetAsync(un)
.ContinueWith(x => {
Console.WriteLine(un + " has been greeted");
}));
await Task.WhenAll(tasks);
}
I'm trying to implement this trivial task of listing all objects in an AmazonS3 bucket with paged requests asynchronously in C#4. I have it working in C#5 using the following snippet:
var listRequest = new ListObjectsRequest().WithBucketName(bucketName);
ListObjectsResponse listResponse = null;
var list = new List<List<S3Object>>();
while (listResponse == null || listResponse.IsTruncated)
{
listResponse = await Task<ListObjectsResponse>.Factory.FromAsync(
client.BeginListObjects, client.EndListObjects, listRequest, null);
list.Add(listResponse.S3Objects);
if (listResponse.IsTruncated)
{
listRequest.Marker = listResponse.NextMarker;
}
}
return list.SelectMany(l => l);
I'm calling the BeginListObjects/EndListObjects pair asynchronously, but I have to repeat that call every time the response says it's truncated. This piece of code works for me.
However, I now want to do this in C#4's TPL, where I don't have the luxury of using async/await and want to understand if this can be done using continuations.
How do I do this same thing in C#4?
Okay, so rather than putting the items into a list with each task/continuation it's easier in a non-await model to just have each task/continuation return the entire sequence. Given that, I used the following helper method to add each one's iterative results onto the aggregate total.
public static Task<IEnumerable<T>> Concat<T>(Task<IEnumerable<T>> first
, Task<IEnumerable<T>> second)
{
return Task.Factory.ContinueWhenAll(new[] { first, second }, _ =>
{
return first.Result.Concat(second.Result);
});
}
Next, I used the follow method to take a task of a single result and turn it into a task of a sequence (containing just that one item).
public static Task<IEnumerable<T>> ToSequence<T>(this Task<T> task)
{
var tcs = new TaskCompletionSource<IEnumerable<T>>();
task.ContinueWith(_ =>
{
if (task.IsCanceled)
tcs.SetCanceled();
else if (task.IsFaulted)
tcs.SetException(task.Exception);
else
tcs.SetResult(Enumerable.Repeat(task.Result, 1));
});
return tcs.Task;
}
Note here that you have some fields/locals not defined; I'm assuming you can add them to the appropriate method without difficulty.
private Task<IEnumerable<S3Object>> method(object sender, EventArgs e)
{
ListObjectsResponse listResponse = null;
return Task<ListObjectsResponse>.Factory.FromAsync(
client.BeginListObjects, client.EndListObjects, listRequest, null)
.ToSequence()
.ContinueWith(continuation);
}
Here is where the real magic happens. Basically,
public Task<IEnumerable<S3Object>> continuation(Task<IEnumerable<S3Object>> task)
{
if (task.Result == null) //not quite sure what null means here//may need to edit this recursive case
{
return Task<ListObjectsResponse>.Factory.FromAsync(
client.BeginListObjects, client.EndListObjects, listRequest, null)
.ToSequence()
.ContinueWith(continuation);
}
else if (task.Result.First().IsTruncated)
{
//if the results were trunctated then concat those results with
//TODO modify the request marker here; either create a new one or store the request as a field and mutate.
Task<IEnumerable<S3Object>> nextBatch = Task<ListObjectsResponse>.Factory.FromAsync(
client.BeginListObjects, client.EndListObjects, listRequest, null)
.ToSequence()
.ContinueWith(continuation);
return Concat(nextBatch, task);//recursive continuation call
}
else //if we're done it means the existing results are sufficient
{
return task;
}
}