1. ホーム
  2. c#

[解決済み] Files.ReadAllLinesの非同期処理と結果待ちを行うには?

2023-07-16 10:37:25

質問

次のようなコードがあります。

    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 にはたくさんの単語があり、私はそれをs変数に読み込んでいます。 asyncawait のキーワードは、C# 5では Async CTP Library を使用して、WPFアプリがハングアップしないようにします。今のところ、私は以下のコードを持っています。

    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;
    }

WPFアプリのフリーズを防ぐために、同期ではなく非同期でファイルを読み込むことが目的です。

どんな助けでも感謝します、ありがとうございます

どのように解決するのですか?

アップデイト : 非同期バージョンの File.ReadAll[Lines|Bytes|Text] , File.AppendAll[Lines|Text]File.WriteAll[Lines|Bytes|Text] は、現在では にマージされ、.NET Core にマージされ、.NET Core 2.0 に同梱されました。また、.NET Standard 2.1 にも含まれています。

使用方法 Task.Run のラッパーで、本質的には Task.Factory.StartNew 非同期ラッパーの場合 はコードの匂いです .

ブロッキング関数を使うことでCPUスレッドを無駄にしたくないのであれば、本当に非同期なIOメソッドを待つべきでしょう。 StreamReader.ReadToEndAsync のような、このようなものです。

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

これは、ファイル全体を string の代わりに List<string> . もし、代わりに行が必要なら、このように後で簡単に文字列を分割することができます。

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

EDIT : と同じコードを実現するための方法をいくつか紹介します。 File.ReadAllLines と同じコードを、真に非同期な方法で実現する方法を紹介します。このコードは、実装が File.ReadAllLines そのものです。

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();
    }
}