본문 바로가기
[2]SW Development Note/[2-1.2]C#

비동기적으로 파일을 처리하는 방법

by 오늘도 빛나는 너에게 2021. 4. 29.
728x90
비동기적으로 파일을 처리하는 방법을 이해하기 위해 다음 단계를 수행한다.
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chapter9.Recipe1
{
       internal class Program
       {
              static void Main(string[] args)
              {
                     var t = ProcessAsynchronousIO();
                     t.GetAwaiter().GetResult();
              }
       const int BUFFER_SIZE = 4096;
       async static Task ProcessAsynchronousIO()
       {
              using (var stream = new FileStream(
                     "test1.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None, BUFFER_SIZE))
              {
                     Console.WriteLine("1. Uses I/O Threads: {0}", stream.IsAsync);
                     byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent());
                     var writeTask = Task.Factory.FromAsync(
                           stream.BeginWrite, stream.EndWrite, buffer, 0, buffer.Length, null);
                     await writeTask;
              }
              using (var stream = new FileStream("test2.txt", FileMode.Create, FileAccess.ReadWrite,
                     FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous))
              {
                     Console.WriteLine("2. Uses I/O Threads: {0}", stream.IsAsync);
                     byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent());
                     var writeTask = Task.Factory.FromAsync(
                           stream.BeginWrite, stream.EndWrite, buffer, 0, buffer.Length, null);
                     await writeTask;
              }
              using (var stream = File.Create("test3.txt", BUFFER_SIZE, FileOptions.Asynchronous))
              using (var sw = new StreamWriter(stream))
              {
                     Console.WriteLine("3. Uses I/O Threads: {0}", stream.IsAsync);
                     await sw.WriteAsync(CreateFileContent());
              }
              using (var sw = new StreamWriter("test4.txt", true))
              {
                     Console.WriteLine("4. Uses I/O Threads: {0}", ((FileStream)sw.BaseStream).IsAsync);
                     await sw.WriteAsync(CreateFileContent());
              }
              Console.WriteLine("Starting parsing files in parallel");
              Task<long>[] readTasks = new Task<long>[4];
              for (int i = 0; i < 4; i++)
              {
                     readTasks[i] = SumFileContent(string.Format("test{0}.txt", i + 1));
              }
              long[] sums = await Task.WhenAll(readTasks);
              Console.WriteLine("Sum in all files: {0}", sums.Sum());
              Console.WriteLine("Deleting files...");
              Task[] deleteTasks = new Task[4];
              for (int i = 0; i < 4; i++)
              {
                     string fileName = string.Format("test{0}.txt", i + 1);
                     deleteTasks[i] = SimulateAsynchronousDelete(fileName);
              }
              await Task.WhenAll(deleteTasks);
              Console.WriteLine("Deleting complete.");
       }
       static string CreateFileContent()
       {
              var sb = new StringBuilder();
              for (int i = 0; i < 100000; i++)
              {
                     sb.AppendFormat("{0}", new Random(i).Next(0, 99999));
                     sb.AppendLine();
              }
              return sb.ToString();
       }
       async static Task<long> SumFileContent(string fileName)
       {
              using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read,
                     FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous))
              using (var sr = new StreamReader(stream))
              {
                     long sum = 0;
                     while (sr.Peek() > -1)
                     {
                           string line = await sr.ReadLineAsync();
                           sum += long.Parse(line);
                     }
                     return sum;
              }
       }
       static Task SimulateAsynchronousDelete(string fileName)
       {
              return Task.Run(() => File.Delete(fileName));
       }
       }
}

결과



예제 분석

이 프로그램을 실행하면 서로 다른 방식으로 4개의 파일을 무작위 데이터로 채운다. 
번째 경우에서는 비동기 프로그래밍 모델 API를 태스크로 변환하는 Filestream 클
래스와 메소드를 사용한다. 두 번째 경우에서는 동일하게 수행하나 Fileoptions.
Asynchronous를 FileStream 생성자에게 제공한다.

FileOptions.Asynchronous 옵션을 사용하는 것이 매우 중요하다. 이 옵션을 빼먹으면 비동기 방식으로 여전히 파일을 처리할 수 있지만, 스레드 풀에서 그냥 대리자를 호축합 뿐이다! 이 옵션(또는 재정의한 다른 생성자인 booluseAsync)을 제공할 때마FileStream 클래스로 I/O 비동기를 사용한다.

세 번째 경우에서는 File.Create 메소드와 StreamWriter 클래스 등의 몇몇 간결한
API를 사용한다. stream.IsAsync 속성을 사용해 확인할 수 있는 I/O 스레드를 여전
히 사용한다. 마지막 경우에서는 지나치게 단순화한 것도 나쁨을 보여준다. 여기서
비동기 대리자 호출의 도움으로 이것을 모방하므로 I/O 비동기를 활용할 수 없다
이제 파일을 읽어 내용을 합친 후 각각 계산을 병렬 비동기로 수행할 수 있다. 마지막
에 모든 파일을 삭제한다. 어떠한 비윈도우 스토어 애플리케이션에서는 비동기적으
로 파일을 삭제할 수 없기 때문에 Task. Run 팩토리 메소드를 사용해 비동기를 모방
한다.


728x90

댓글