2012-10-31 6 views
43

I次のコードは、 Files.ReadAllLinesを非同期化して結果を待っていますか?

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

が、私は5ので、WPFアプリはdoesnの Async CTP Libraryを使用してC#で asyncawaitキーワードを使用するようにしようとしています、私はsの変数に読み込む言葉のトンを持っていハングアップしません。これまでのところ、私は次のコードを持って、

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アプリの凍結を避けるために、というよりも、同期、非同期にファイルを読むことです。

ありがとうございました。

+1

文字列配列のコピーを作成するToList()への不要な呼び出しを削除することから始めるのはどうでしょうか? –

+2

@JbEvain - 児童のために、ToList()は配列をコピーするだけではなく、 'List'を作成します。それ以上の情報がなければ、おそらく '' // do something with s'は '' List'メソッドを呼び出すので、その不要なものを仮定することはできません。 – Mike

答えて

80

UPDATEFile.ReadAll[Lines|Bytes|Text]File.AppendAll[Lines|Text]File.WriteAll[Lines|Bytes|Text]の非同期バージョンは今merged into .NET Coreされています。これらのメソッドは、.NET Framework、Monoなどに移植され、.NET標準の将来のバージョンに組み込まれる予定です。

Task.Run(本質的にはTask.Factory.StartNewのラッパー)を使用すると、非同期ラッパーis a code smellになります。

あなたが遮断機能を使って、CPUスレッドを無駄にしたくない場合は、あなたが本当に非同期IO方式を待つ必要があり、StreamReader.ReadToEndAsync、このような:

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

これは、ファイル全体を取得しますList<string>の代わりに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の実装自体に基づいています。これが動作するためにあなたが試して、最終的には必要ありません

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

編集:

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

私はこの感謝について知らなかったkhellang :) –

+6

これは、重要なのは、CPUスレッドを使わずにこれを待つためにWindows I/Oポートを使用するのに対し、別の答えでTask.Factory.StartNew/Task.RunのアプローチはCPUスレッドを浪費します。この回答のアプローチはより効率的です。 –

+1

FYI; https://github.com/dotnet/corefx/issues/11220でこれらのAPIの非同期バージョンを提案しました。どうすればいいのか見てみましょう:) – khellang

-3

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

+0

チャームのように働いた!ありがとうマイク、 'Task.Factory.StartNew(()=> 'Some Task')'を他のタスクにも適用できました。ありがとうございました:) –

+10

これは確かに最も簡単な解決策ですが、シンプルなGUIアプリケーションでは、まだスレッドをブロックしているので、完全な可能性に対して 'async'を使用しません。 – svick

+0

また、 'Task.Run()'を使ってコードを短縮することもできます。 – svick

-3

あなたの質問に記載されている問題も発生しました。

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); 
+0

'ApplicationData'と' FileIO'クラスはどこから来ますか?彼らは.NET Frameworkの一部ではないようです。 'ApplicationData'は[UWP Framework](https://docs.microsoft.com/en-us/uwp/api/windows.storage.applicationdata)のものです。つまり、 "通常の" .netアプリケーションでは 'ApplicationData'を使うことはできません。 'FileIO'は[VisualBasicアセンブリ](https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.filesystem(v = vs.110).aspx)に存在しますが、私が見る限りでは非同期メソッドなので、どこから取得していますか? – AndyJ

+0

@AndyJ、はい私の解決策はUWPアプリケーションです。 –

関連する問題