Tips for using async/await - c#

I have been reading up on asynchronous programming in C# for the last few days. The reason for this is that the filepicker in UWP (necessary because I am programming for the Hololens 2 in Unity) is asynchronous. Despite many sources, i.e. 1, 2, 3, I have some understanding problems regarding the correct use and maybe experienced users here have a few tips for me.
An example of what I am trying to do:
Start of the programm:
public class SceneUIHandler : MonoBehaviour
{
openFile()
{
pickFile(); // calls await filepicker.PickSingleFileAsync();
loadData(path); // calls FileLoader.loadFile with path from filepicker for the reader
showData(); // should display the data
}
}
The Loader:
public class FileLoader:
{
async void loadFile(string path)
{
Task<StreamReader> streamReaderTask = getStreamReader(filePath);
StreamReader sr = await streamReaderTask;
// after that read file with the StreamReader...
...
}
async Task<StreamReader> getStreamReader(string path)
{
StorageFile file = await StorageFile.GetFileFromPathAsync(path);
var randomAccessStream = await file.OpenReadAsync();
Stream stream = randomAccessStream.AsStreamForRead();
StreamReader str = new StreamReader(stream);
return str;
}
}
So I get the path with the filepicker and later in the FileLoader class I call the file with the path and create a streamreader. So far everything works.
My problem is that if the creation of the reader takes longer the code stops because of await and jumps accordingly again in openFile() in SceneUIHandler after the method loadData(). But after that comes showData() and here I expect that the data is already loaded. From the tutorials I would now make openFile() async to write an await loadData() here.
If I do that I have to async one method after the other because I have to wait somewhere until the data is loaded otherwise i can't display, change, interact with the (not loaded) data. But by async + await I wait and continue in the previous method (which also relies on the data).
How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
I also get now a Cross thread invocation exception probably by creating and calling a slighty different getStreamReader(string path) method which just returns a BinaryReader instead of a StreamReader.

I also recommend reading my async best practices article. The first two best practices are still the most important.
From the tutorials I would now make openFile() async to write an await loadData() here.
The first best practice is "Avoid async void", because your code can't know when the method finishes. In other words, use async Task instead of async void, and await that method call.
If I do that I have to async one method after the other because I have to wait somewhere until the data is loaded otherwise i can't display, change, interact with the (not loaded) data. But by async + await I wait and continue in the previous method (which also relies on the data).
How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
The second best practice is "Use async all the way". It's normal to feel this is weird at first, but it is the correct procedure.
Eventually, you'll yield back to your framework. In the case of UWP/Unity, you'll have a UI at your highest level. And what you'll have to do is show some kind of "Loading..." screen (immediately and synchronously), and then update that when the asynchronous work completes. I have an article on async data binding that's written from an MVVM/WPF perspective, but the same ideas translate to any UI framework.

How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
Just from my point of view, the code that relies on the data needs to wait for the result, other code could run separately.
So something like this:
openFile()
{
// if it is a task
var tas = pickFile();
// TD
somework that not related to the file you get.
like setting UI size, change layout
textblock.text="this is the file";
textblock.background="";
//when all is done wait to get the file
string filepath = await task;
// code that needs the data
LoadAndShowData(filepath);
}

Related

Having trouble refactoring synchronous app to use async/await (C#)

I'm trying to refactor an existing synchronous app into one that uses Async/Await. This WPF app was written originally using a BackgroundWorker thread calling a bunch of synchronous methods. I went down to the deepest level and worked my way up, converting these methods to async, using await Task.Run(()..) on some of them that didn't seem to be taking too long.
I am stuck on something though. I want to pass in an IProgress parameter, to be able to get feedback into the UI -- and the last, deepest method, the one that takes the longest to execute, is written this way:
public static Task<bool> SaveToFileAsync(string filePath)
{
return Task.Run(() => SaveToFile(filePath));
/*
SaveToFile calls into an unmanaged dll that uses a few loops and writes
to file using fopen/fwrite...
*/
}
private async void CreateObjectAndSaveToFile(/*IProgress progress*/)
{
List<Task> SaveFileTaskArray = new List<Task>();
int index = 0;
foreach (...)
{
//
{
DoSomethingThatTakesSomeTime();
//Start Async Task to save this SomeThing to file
SaveFileTaskArray.Add(SaveToFileAsync(fileName));
}
index++;
}
await Task.WhenAll(SaveFileTaskArray);
}
Now if I call CreateObjectAndSaveToFile() on the UI side like so:
await CreateObjectAndSaveToFile();
I get the desired effect - UI is responsive and files get saved one by one (can be 10-20 files). But for completeness, I'd like to add a progress bar to this (and therefore adding that IProgress parameter all the way down).
I'm not sure how I can do that.
You mention in the comments that you know how to use IProgress already. So if you want report progress as each task finishes, you can use WhenAny() instead of WhenAll(), and remove each task from the list as it finishes. Something like this:
while (SaveFileTaskArray.Count > 0) {
var finishedTask = await Task.WhenAny(SaveFileTaskArray);
SaveFileTaskArray.Remove(finishedTask);
//Report progress of how many are left
progress.Report(SaveFileTaskArray.Count);
}
The Microsoft documentation actually has a whole article about this, if you want to read more: Start Multiple Async Tasks and Process Them As They Complete

C# Windows application show picture box until background process finish

I have to call my API and show waiting PictureBox(wait.gif) until API get complete and return the data to bind my custom control into tablepanellayout.
I can't use BackgroundWorker class here because it has cross-thread issue with my sub control in custom control.
Here I have only idea to do that, is call child thread from main thread but until its get executed completely show the PictureBox(wait.gif) and block the rest code to be executed.
Can anybody suggest me how to do it exactly or please provide some code snippet for example.
There are two articles that helped me a lot understanding async-await:
This interview with Eric Lippert. Search somewhere in the middle for async aawait. Eric compares async await with a cook who doesn't wait for the water to boil, but instead looks around if he can do something else in the mean time.
Async Await by the ever so helpful Stephen Cleary
For WinForms you do the following:
If your data query function has async versions to fetch the data, use that function. Think of functions like SqlConnection.OpenAsync, Dapper.QueryAsync etc
If your data querier has no async versions make it async using Task.Run
Every function that calls async functions should be declared async itself
Every async function returns Task instead of void and Task<TResult> instead of TResult.
The only exception is the event handler: this function returns void instead of Task
the calls to other async functions should be awaited before your async returns.
.
// The event handler: make async. The only one that still returns void
async void OnButton1_clicked(object sender, ...)
{
// start fetching the data. Don't await yet, you'll have other things to do
Task<MyData> fetchDataTask = FetchData(...);
// meanwhile: show the user that you are busy:
this.ShowBusy(true); // show picture box?
// if needed do other things you can do before the data is fetched
this.ClearTable();
// once you have nothing meaningful to do, await for your data
MyData fetchedData = await fetchDataTask;
this.ProcessData(fetchedData);
// finished:
this.ShowBusy(false); // remove picture box
}
Async version of the function that fetched the data:
async Task<IQueryable<MyData>> FetchDataAsync(myParams)
{
using (SqlConnection dbConnection = new SqlConnection(...)
{
// open the connection, don't wait yet:
Task taskOpen = sqlCommand.OpenAsync();
// continue while opening:
using (var sqlCommand = new SqlCommand(...))
{
cmd.Parameters.AddWithValue(...);
// before executing the query: wait until OpenAsync finished:
await taskOpen;
// read the data. If nothing to do: await, otherwise use Task similar to Open
SqlDataReader dataReader = await cmd.ExecuteReaderAsync();
foreach (var row in dataReader)
{
... (some Await with GetFieldValueAsync
}
}
}
}
I'm not really familiar with reading SQL data on such a low level, I prefer entity framework and dapper, so there might be an error in my SqlReader stuff. Maybe someone can correct this. Still you'll get the gist.
If you write it like this, your program will be pretty responsive: Whenever the procedure has to await for something, control is given back to the caller of the function who can continue processing until he meets an await, when control is given back to the caller, who continues processing, etc.
Note that this won't help you if your program is not waiting for something. If your main thread does some heavy calculations for several seconds, your program won't be responsive. Consider creating an async function that will do this for your using Task.Run
If you have programmed like this, all threads performing your functions will have the same context: it will be as if only the UI-thread is involved. No need for mutexes, semaphores, InvokeRequired etc.

Async Deserialization call not working in C#

I am using Newtonsoft.Json for reading a json file. I am trying to make a aysnc call to the json file to read its data but unfortunately it's not returning anything. I tried without async and it works perfectly, following is my code:
public static async Task<T> LoadAsync<T>(string filePath)
{
// filePath: any json file present locally on the disk
string basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\", "");
string fullPath = Path.Combine(basePath, filePath);
using (var stream = File.OpenRead(fullPath))
{
var reader = new StreamReader(stream, Encoding.GetEncoding("iso-8859-1"));
var task = Task.Factory.StartNew(() => JsonConvert.DeserializeObject<T>(reader.ReadToEnd()));
var value = await task;
return value;
}
}
I tried to debug but debugger is not coming on "return value" in the above method and I am calling above method by following function:
private void GetDataFromJson()
{
var value = JsonUtilities.LoadAsync<TaxOffice>(Constant.TAXJSONINPUTFILE);
}
What can be the problem ? File is present locally on my computer.
I am trying to make a aysnc call to the json file to read its data
Do you really want to make the code asynchronously? Does the JsonUtilities class offer a synchronous version of the LoadAsync() method?
Your method is synchronous:
private void GetDataFromJson()
{
var value = JsonUtilities.LoadAsync<TaxOffice>(Constant.TAXJSONINPUTFILE);
}
It does exactly one thing: it calls LoadAsync(). It does store the return value of that method in value, but you never use value. So it's ignored. The return value of LoadAsync() is not the TaxOffice object. It's a Task that represents the work LoadAsync() is doing. Until that task is done, there's no way to get a value. But GetDataFromJson() doesn't wait for the task to be done. So if the caller expects it to be done by the time the method returns, it's going to be sorely disappointed.
How best to fix your code is unclear, as you haven't provided a good, minimal, complete code example showing what you need help with. But there are two obvious strategies you can follow:
Make the method asynchronous:
private async Task GetDataFromJson()
{
var value = await JsonUtilities.LoadAsync<TaxOffice>(Constant.TAXJSONINPUTFILE);
// presumably you do something with "value" here?
}
This is the best approach. But it will require that the caller be able to correctly deal with an asynchronous call. It will likely need to be turned into an async method as well. And its caller. And so on, until you get to the top of your call stack (e.g. an event handler method).
It's a bit of a pain to switch to async throughout your call stack, but the code will work much better if you do. Your thread (probably a UI thread) won't get stuck waiting on the operation, and you'll be all set to correctly deal with other asynchronous operations as well.
Ignore the asynchronous nature of the LoadAsync() method:
private void GetDataFromJson()
{
var value = JsonUtilities.LoadAsync<TaxOffice>(Constant.TAXJSONINPUTFILE).Result;
// presumably you do something with "value" here?
}
This is the Not Very Good™ approach. It works. But it holds up your current thread until the asynchronous operation is done, negating the entire benefit of having an asynchronous operation in the first place.

How to read data from stream asychronously in background

I would like to read data from a stream (serial, tcp, whatever) asynchronously and trigger events to notify other components.
Following is pseudo code, assuming "stream" is a valid stream.
public class Reader {
private _stream;
private _buffer = new bytes[4096];
public event Action<byte[], int> DataRecieved;
public async void StartReading() {
while (true) {
var nbytes = await _stream.ReadAsync(_buffer, 0, _buffer.Length);
if (nbytes == 0)
return;
var handlers = DataRecieved;
if (handlers != null)
DataRecieved(_buffer, nbytes);
}
}
}
And the caller part:
var r = new Reader();
r.OnDataRecieved += myHandler;
r.StartReading();
I'm not sure doing something like this is a good idea. I read that using asynchonous void functions is not a good idea, but here I don't want caller to wait for the result, I want to notify it when some data is available.
What's the good way to do something like that ?
void async is only considered to be used for GUI event handlers. In WinForms, events have all delegate-types of type void. Usually, you want, when using async , notify your caller when you have finished - in an asynchronous way. The .NET message-loop is considered the exception here, since you have no different possibility to use async.
In your case, the async/await keywords won't make much sense. I'd recommend to invoke your method using a Task or the ThreadPool (or BackgroundWorker).
You do not have a long running task on which you want to react in a asynchronous manner, but a parallel background-task, which should be used as such.
The idea of async/await is that the caller of a method continues after the method invocation and may execute code inside the method behind an await later. But that requires you to use await in the calling-method, which would block your main-thread.
Long story short: You have no other chance as using a second thread and use thread-synchronization.
Invoke is nothing else as placing a delegate in a queue which the message-loop reads and executes. In your case, you could do something similar: Take the read data, like the byte[] and put that in a queue (via the event). And whenever your main-thread desires to do some work, he grabs an item from the queue.
That is one option. The best solution for this issue depends strongly on your application, and as far as you didn't tell us more details about the design, I can't recommend the best way. But async/await won't be it. Definitely.
Make the method return Task to avoid async void. You can ignore that task if you want but eventually you probably want to wait for it to complete.
Also handle errors. Right now they are thrown away and the reading stops silently. You will never find bugs this way.
Wrap everything in Task.Run to make sure that this async method really runs completely asynchronously. Right now if each await completes right away this method will never return or not return in a long time. Don't risk that. Alternatively you can place await Task.Yield(); at the first line of the method.

Reading a text file and code jumps when using await

I am writing an app for windows 8.1 tablet and I am trying to read data from a text file I have saved (this text file is just over 1kb). The below code I have works on some occasions but mainly fails(debugging/stepping over the code will often see it succeed).
StorageFolder folder = ApplicationData.Current.LocalFolder;
string fileName = "collections.json";
public Dictionary<string, string> masterDataObject;
private async void CallLoad()
{
await this.Load();
}
public async Task<Dictionary<string, string>> Load()
{
//create a file
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
string fileContents = string.Empty;
if (file != null)
{
//returns a JSON string
fileContents = await FileIO.ReadTextAsync(file);//WHY DOES IT JUMP AT THIS LINE?????
}
Dictionary<string, string> customerFromJSON = JsonCrt.DeserializeObject<Dictionary<string, string>>(fileContents) ?? new Dictiy<string, string>();
masterDataObject = customerFromJSON;
return masterDataObject;
}
After stepping through the code several times I can see at the line
fileContents = await.....
it drops out of the method and continues with the rest of the code that is not in my Load() method.
I have reviewed async and await and have really tried to understand it, but my understanding leaves me confused as to why this is happening.
I was under the belief that await meant that execution would stop until you get a response from the code you are calling (obviously not).
So how should I be using async to make it work properly, or is there a better alternative than this code?
I have looked and tried many options but so had no success, any advice would be gratefully received.
it drops out of the method and continues with the rest of the code that is not in my Load() method
That's exactly what it's ment to do, yield control to the calling site until the asynchronous operation completes.
I'm assuming your problem lays with the fact your CallLoad method is async void instead of async Task, thus cannot be awaited, and that's why you're seeing it continue without waiting for the internal async IO to complete.
I was under the belief that await meant that execution would stop until you get a response from the code you are calling
That is still entirely true, if you await the async operation all the way
So how should I be using async to make it work properly?
async void is ment only for top level event handlers. If CallLoad isn't used as one, it should be async Task and awaited: await CallLoad();
What await does behind the scenes:
it creates a callback which contains the code after await
it calls into the xyzAsync() function, registering the callback.
if the asynchronous operation finishes, the callback is called.
So if you step through the code with a debugger, it will execute some other code if it hits await returning to the code after the await as soon as the asynchronous operation has finished.

Categories