How to Async Files.ReadAllLines and await for resu

2019-01-22 01:03发布

I have the following code,

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        button1.IsEnabled = false;

        var s = File.ReadAllLines("Words.txt").ToList(); // my WPF app hangs here
        // do something with s

        button1.IsEnabled = true;
    }

Words.txt has a ton of words which i read into the s variable, I am trying to make use of async and await keywords in C# 5 using Async CTP Library so the WPF app doesn't hang. So far I have the following code,

    private async void button1_Click(object sender, RoutedEventArgs e)
    {
        button1.IsEnabled = false;

        Task<string[]> ws = Task.Factory.FromAsync<string[]>(
            // What do i have here? there are so many overloads
            ); // is this the right way to do?

        var s = await File.ReadAllLines("Words.txt").ToList();  // what more do i do here apart from having the await keyword?
        // do something with s

        button1.IsEnabled = true;
    }

The goal is to read the file in async rather than sync, to avoid freezing of WPF app.

Any help is appreciated, Thanks!

4条回答
别忘想泡老子
2楼-- · 2019-01-22 01:26

Try this:

private async void button1_Click(object sender, RoutedEventArgs e)
{
    button1.IsEnabled = false;
    try
    {
        var s = await Task.Run(() => File.ReadAllLines("Words.txt").ToList());
        // do something with s
    }
    finally
    {
        button1.IsEnabled = true;
    }
}

Edit:

You don't need the try-finally for this to work. It's really only the one line that you need to change. To explain how it works: This spawns another thread (actually gets one from the thread pool) and gets that thread to read the file. When the file is finished reading then the remainder of the button1_Click method is called (from the GUI thread) with the result. Note that this is probably not the most efficient solution, but it is probably the simplest change to your code which doesn't block the the GUI.

查看更多
Deceive 欺骗
3楼-- · 2019-01-22 01:27

I also encountered a problem described in your question. I've solved it just simplier that in previous answers:

string[] values;
StorageFolder folder = ApplicationData.Current.LocalFolder; // Put your location here.
IList<string> lines = await FileIO.ReadLinesAsync(await folder.GetFileAsync("Words.txt"););
lines.CopyTo(values, 0);
查看更多
可以哭但决不认输i
4楼-- · 2019-01-22 01:35

Use Stream.ReadAsync for asynchronous reading of file,

private async void Button_Click(object sender, RoutedEventArgs e)
{
    string filename = @"c:\Temp\userinputlog.txt";
    byte[] result;

    using (FileStream SourceStream = File.Open(filename, FileMode.Open))
    {
        result = new byte[SourceStream.Length];
        await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
    }

    UserInput.Text = System.Text.Encoding.ASCII.GetString(result);
}

Read MSDN Stream.ReadAsync

查看更多
Viruses.
5楼-- · 2019-01-22 01:37

UPDATE: Async versions of File.ReadAll[Lines|Bytes|Text], File.AppendAll[Lines|Text] and File.WriteAll[Lines|Bytes|Text] have now been merged into .NET Core. These methods will hopefully be ported back to .NET Framework, Mono etc. and rolled into a future version of .NET Standard.

Using Task.Run, which essentially is a wrapper for Task.Factory.StartNew, for asynchronous wrappers is a code smell.

If you don't want to waste a CPU thread by using a blocking function, you should await a truly asynchronous IO method, StreamReader.ReadToEndAsync, like this:

using (var reader = File.OpenText("Words.txt"))
{
    var fileText = await reader.ReadToEndAsync();
    // Do something with fileText...
}

This will get the whole file as a string instead of a List<string>. If you need lines instead, you could easily split the string afterwards, like this:

using (var reader = File.OpenText("Words.txt"))
{
    var fileText = await reader.ReadToEndAsync();
    return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}

EDIT: Here are some methods to achieve the same code as File.ReadAllLines, but in a truly asynchronous manner. The code is based on the implementation of File.ReadAllLines itself:

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public static class FileEx
{
    /// <summary>
    /// This is the same default buffer size as
    /// <see cref="StreamReader"/> and <see cref="FileStream"/>.
    /// </summary>
    private const int DefaultBufferSize = 4096;

    /// <summary>
    /// Indicates that
    /// 1. The file is to be used for asynchronous reading.
    /// 2. The file is to be accessed sequentially from beginning to end.
    /// </summary>
    private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;

    public static Task<string[]> ReadAllLinesAsync(string path)
    {
        return ReadAllLinesAsync(path, Encoding.UTF8);
    }

    public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding)
    {
        var lines = new List<string>();

        // Open the FileStream with the same FileMode, FileAccess
        // and FileShare as a call to File.OpenText would've done.
        using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions))
        using (var reader = new StreamReader(stream, encoding))
        {
            string line;
            while ((line = await reader.ReadLineAsync()) != null)
            {
                lines.Add(line);
            }
        }

        return lines.ToArray();
    }
}
查看更多
登录 后发表回答