80

私は次のコードを持っています、

    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.txts 変数に読み込む大量の単語があります。WPF アプリがハングしないように、C# 5でキーワードasyncとキーワードを使用しようとしています。これまでのところ、次のコードがあります。awaitAsync CTP Library

    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 アプリのフリーズを回避することです。

どんな助けでも大歓迎です、ありがとう!

4

5 に答える 5

152

更新:の非同期バージョンであり、File.ReadAll[Lines|Bytes|Text].NET Coreにマージされ、.NETCore2.0に同梱されています。これらは、.NETStandard2.1にも含まれています。File.AppendAll[Lines|Text]File.WriteAll[Lines|Bytes|Text]

Task.Run本質的にのラッパーであるTask.Factory.StartNew、を非同期ラッパーに使用することは、コードの臭いです。

ブロッキング関数を使用してCPUスレッドを無駄にしたくない場合は、次StreamReader.ReadToEndAsyncのような真の非同期IOメソッドを待つ必要があります。

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

編集:これは、と同じコードを実現するためのいくつかの方法です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();
    }
}
于 2012-10-31T21:54:42.567 に答える
-3

これを試して:

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

編集:

これが機能するために、最後に試してみる必要はありません。変更する必要があるのは、実際には1行だけです。それがどのように機能するかを説明するために:これは別のスレッドを生成し(実際にはスレッドプールから1つを取得します)、そのスレッドにファイルを読み取らせます。ファイルの読み取りが終了すると、button1_Clickメソッドの残りの部分が(GUIスレッドから)呼び出され、結果が得られます。これはおそらく最も効率的な解決策ではないことに注意してください。ただし、GUIをブロックしない、コードへの最も簡単な変更である可能性があります。

于 2012-10-31T21:54:49.317 に答える