2012-10-31 2 views
43

나는 다음과 같은 코드가 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 응용 프로그램은 아무튼 Async CTP Library를 사용하여 C#에서 asyncawait 키워드의 사용을 만들려고 노력하고 내가의 변수로 읽어 단어의 톤을 가지고 있습니다 멈추지 않아. 지금까지 나는 다음과 같은 코드를 가지고,

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

UPDATE : File.ReadAll[Lines|Bytes|Text], File.AppendAll[Lines|Text]File.WriteAll[Lines|Bytes|Text]의 비동기 버전은 지금 merged into .NET Core을이었다. 이 메서드는 .NET Framework, Mono 등으로 포팅되어 .NET 표준의 향후 버전으로 통합 될 것입니다.

Task.Run은 비동기 래퍼 is a code smell의 경우 본질적으로 Task.Factory.StartNew의 래퍼입니다.

당신이 차단 기능을 사용하여 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); 
} 

편집을 : 여기 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(); 
    } 
} 
+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

이 시도 :

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

편집 :

이 작업을 당신은-마지막 시도가 필요하지 않습니다. 정말로 변경해야 할 한 줄입니다. 어떻게 작동하는지 설명하기 : 이것은 다른 스레드를 생성하고 (실제로 스레드 풀에서 스레드를 가져옵니다) 스레드를 가져 와서 파일을 읽습니다. 파일 읽기가 끝나면 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 프레임 워크] (https://docs.microsoft.com/en-us/uwp/api/windows.storage.applicationdata)에서 나온 것으로 보입니다. 이는 "일반".net 어플리케이션에서'ApplicationData'를 사용할 수 없다는 것을 의미합니다. 'FileIO'는 [VisualBasic assembly] (https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.filesystem (v = vs.110) .aspx)에 존재하지만, 비동기 메서드는 내가 볼 수있는 한 멀리, 그래서 어디에서 가져 오는거야? – AndyJ

+0

@AndyJ, 맞습니다. 내 솔루션은 UWP 응용 프로그램입니다. –