Progress reporting with async/await is not working
Asked Answered
C

2

4

I have a C# WPF program that opens a file, reads it line by line, manipulates each line then writes the line out to another file. That part worked fine. I wanted to add some progress reporting so I made the methods async and used await with progress reporting. The progress reporting is super simple - just update a label on the screen. Here is my code:

async void Button_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog openFileDialog = new OpenFileDialog();
    openFileDialog.Title = "Select File to Process";
    openFileDialog.ShowDialog();
    lblWaiting.Content = "Please wait!";
    var progress = new Progress<int>(value =>
    {
        lblWaiting.Content = "Waiting "+ value.ToString();
    });
    string newFN = await FileProcessor(openFileDialog.FileName, progress);
    MessageBox.Show("New File Name " + newFN);
} 
     
static async private Task<string> FileProcessor(string fn, IProgress<int> progress)
{
    FileInfo fi = new FileInfo(fn);
    string newFN = "C:\temp\text.txt";

    int i = 0;

    using (StreamWriter sw = new StreamWriter(newFN))
    using (StreamReader sr = new StreamReader(fn))
    {
        string line;
         
        while ((line = sr.ReadLine()) != null)
        {
            //  manipulate the line 
            i++;
            sw.WriteLine(line);
            // every 500 lines, report progress
            if (i % 500 == 0)
            {
                progress.Report(i);
            }
        }
    }
    return newFN;
}

But the progress reporting is not working.

Any help, advice or suggestions would be greatly appreciated.

Cavort answered 30/9, 2017 at 1:3 Comment(0)
T
6

Just marking your method as async has pretty much no effect on execution flow, because you're not yielding the execution ever.

Use ReadLineAsync instead of ReadLine and WriteLineAsync instead of WriteLine:

static async private Task<string> FileProcessor(string fn, IProgress<int> progress)
{
    FileInfo fi = new FileInfo(fn);
    string newFN = "C:\temp\text.txt";

    int i = 0;

    using (StreamWriter sw = new StreamWriter(newFN))
    using (StreamReader sr = new StreamReader(fn))
    {
        string line;

        while ((line = await sr.ReadLineAsync()) != null)
        {
            //  manipulate the line 
            i++;
            await sw.WriteLineAsync(line);
            // every 500 lines, report progress
            if (i % 500 == 0)
            {
                progress.Report(i);
            }
        }
    }
    return newFN;
}

That will yield the UI thread and allow the label to be redrawn.

PS. The compiler should have raised a warning with your initial code, because you have an async method which does not use await.

Trivalent answered 30/9, 2017 at 1:14 Comment(2)
OMG! It worked!!! I'm so excited I'm finally starting to understand this async stuff. Thank you, thank you, thank you!!!!Cavort
Question... in my case, my app is just doing a crap-ton of CPU-bound calculations so there's no ReadLineAsync/WriteLineAsync I can await. I thought this was where you use await Task.Yield() after updating the progress, but that didn't work. I ended up using await Task.Delay(1) which did work, but just feels soooo wrong to me! So what is the proper way for a purely CPU-bound task? If it makes any difference, it's started with this (renamed for clarity)... var result = await Task.Run(() => HeavyCPUTask(progress));Adhern
M
1

Using asynchronous I/O APIs, as suggested by MarcinJuraszek, solves the problem, but the built-in .NET asynchronous I/O APIs are not as performant as the corresponding synchronous APIs, so you may prefer not to use them. An alternative is to offload the invocation of the FileProcessor to the ThreadPool, using the Task.Run method:

string newFN = await Task.Run(() => FileProcessor(openFileDialog.FileName, progress));

This way you can ensure that the performance will be preserved, that the progress reporting will work, and that the UI thread will not be blocked (unless you report progress too frequently, which I can see that you are careful enough not to do).

Mccool answered 31/5 at 5:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.