2

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.

Alex
  • 151
  • 10
  • Maybe take a look at this: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/ – Ax1le Aug 03 '22 at 08:20
  • 1
    @Ax1le Thanks, I did read this article. But from what I understood, I have to propagate the async to the very last method (= in this case the main)!? – Alex Aug 03 '22 at 08:52
  • Also in their example: Task eggsTask = FryEggsAsync(2); Task baconTask = FryBaconAsync(3); Task toastTask = ToastBreadAsync(2); Toast toast = await toastTask; ... Juice oj = PourOJ(); Egg eggs = await eggsTask; Bacon bacon = await baconTask; Would the code continue after the await toastTask? In my understanding it would stop until the toast is ready, makes the juice, stop until eggs are ready and the finally stop until the bacon is ready - But of course Eggs & Bacons would still run during the whole process (just visible one after the other). – Alex Aug 03 '22 at 08:53
  • There is a while loop for the `breakfastTasks`, so even when the `toastTask` is finished, you still need to wait for `baconTask` and `eggsTask `. When all the task finishes, it will make juice. You could run the sample in a console app which will help you understand it better. – Ax1le Aug 03 '22 at 09:17
  • Sry i had to point out that i meant the code from section "Start tasks concurrently". The final idea to wait with Task.WhenAny() is clear to me. But you are right i will run it myself. – Alex Aug 03 '22 at 11:05
  • Btw. StreamReader needs to be disposed (maybe with `using (StreamReader sr =` – tymtam Aug 04 '22 at 02:26
  • Have you seen: https://stackoverflow.com/questions/21261939/how-to-execute-async-task-in-unity3d – tymtam Aug 04 '22 at 02:31

2 Answers2

4

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.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

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); 
    }
Roy Li - MSFT
  • 8,043
  • 1
  • 7
  • 13