10/18 비동기

정수현·2024년 10월 19일

C#

목록 보기
4/10
post-thumbnail

동기 방식

static void Main(string [] args)
{
    using (FileStream fs = new FileStream("...", 
            FileMode.Open, File.Access.Read, FileShare.ReadWrite)) //파일 객체 생성
    {
        byte[] buf = new byte[fs.Length];
        fs.Read(buf, 0, buf.Length);
        
        string txt = Encoding.UTF8.GetString(buf);
        Console.WriteLine(txt);
    }
}
  • 스레드가 Read 메서드를 완료한 후 파일의 내용을 화면에 출력하는 코드를 순차적으로 실행한다.

비동기 방식

기존의 비동기 방식

private static void AsyncRead()
{
    FileStream fs = new Filestream("...", FileMode.Open, File.Access.Read, FileShare.ReadWrite);
    
    FileState state = new FileState();
    state.Buffer = new byte[fs.Length];
    state.File = fs;
    
    fs.BeginRead(state.Buffer, 0, sate.Buffer.Length, readCompleted, state);
    //Read가 완료된 후의 코드를 readCompleted로 넘겨서 처리한다.
    
    Console.ReadLine();
    fs.Close();
}
  • BeginRead 메서드를 호출했을 때 Read 동작 이후의 코드를 별도로 분리해 readCompleted 메서드를
    처리해야 한다는 불편함이 있다.
static void readCompleted(IAsyncResult ar)
{
    FileState state = ar.AsyncState as FileState;
    state.File.EndRead(ar);
    string txt = Encoding.UTF8.GetString(state.Buffer);
    Console.WriteLine(txt);
}
  • 읽기 작업이 완료되면 스레드 풀의 자유 스레드가 readCompleted 메서드를 실행시킨다.

  • 동기 방식을 비동기 방식으로 바꾸기 위해서는 Read 호출 이후의 코드를 BeginRead에 전달한다. (콜백)


C#에서 지원하는 비동기 방식

  • Read 호출 이후의 코드를 따로 전달하는 번거로움을 없앤다.
  • async await 키워드를 사용한다.
    async : 해당 메서드가 비동기 작업을 수행할 수 있음을 명시한다.
    await : 비동기 작업의 완료를 기다린 후 다음 코드를 실행하게 만든다. 이로 인해 프로그램이 중지되지 않고 비동기로 동작할 수 있다.
private static async void AwaitRead()
{
    using (FileStream fs = new FileStream("...", FileMode.Open, File.Access.Read, FileShare.ReadWrite)) //파일 객체 생성
    {
        byte[] buf = new byte[fs.Length]; //파일 크기만큼의 바이트 배열 생성
        Console.WriteLine("Before ReadAsync: " + Thread.CurrentThread.ManagedThreadId);
        
        //---------------------------------------------------------------------------//
        
        await fs.ReadAsync(buf, 0, buf.Length);
        Console.WriteLine("After ReadAsync: " + Thread.CurrentThread.ManagedThreadId);
        
        string txt = Encoding.UTF8.GetString(buf);
        Console.WriteLine(txt);
    }
}

비동기 방식 설정

  • 인자 : 시작 주소, 시작 위치, 사이즈
  • 컴파일러는 await 이후의 코드를 묶어서 ReadAsync의 비동기 호출이 끝난 후 실행되도록 한다.

흐름

  • 비동기 작업이 시작되면, 기존 스레드는 그 작업이 완료될 때까지 다른 작업을 수행할 수 있다.
  • 비동기 작업이 완료되면, await 뒤의 코드가 실행된다.
  • ReadAsync가 완료되면, "After ReadAsync : " 문장이 출력되고,
  • 이때의 스레드 ID는 비동기 작업이 완료된 후의 또 다른 스레드이다.



💡 정리 💡

① 동기 방식

  • 한 줄의 코드가 끝날때까지 다음 코드로 넘어가지 않는다.
WebClient wc = new WebClient();
string txt = wc.DownloadString(" ... ");
Console.WriteLine(txt);
  • wc.DownloadString(" ... ");, 웹 페이지에서 문자열을 다운로드 할 때까지 프로그램이 멈춘다.
  • 다운로드가 완료된 후에만 다음 코드(Console.WriteLine(txt);)를 실행한다.

② 비동기 방식

  • 시간이 오래 걸리는 작업(ex.파일 다운로드, 네트워크 요청 등)이 수행되는 동안 메인 스레드가 해당 작업을 기다리지 않고 다른 작업을 계속 진행할 수 있다.
  • 작업이 진행되는 동안 다른 작업을 병렬로 수행할 수 있다.
  • 특정 작업이 완료될 때까지 프로그램의 실행을 멈추지 않는다.
  • 기존의 비동기 방식에서는 작업이 끝나면 Callback 함수를 호출하여 프로그램이 멈추거나 응답하지 않는 문제를 방지한다.
static void Main(string [] args)
{
    WebClient wc = new WebClient(); //객체 생성
    
    //이벤트 핸들러 함수 (콜백)
    wc.DownloadStringCompleted += wc_DownloadStringCompleted;
    
    //비동기적으로 문자열 다운로드
    wc.DownloadStringAsync(new Uri(" ..." );
    
    Console.ReadLine();
}

static void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    //다운로드된 HTML 텍스트 출력
    Console.WriteLine(e.Result);
}
  • 위 코드는 콜백 기반의 비동기 프로그램이다.

  • 이벤트 핸들러를 통해 비동기 작업 완료 시 호출될 메서드를 등록한다. (함수 실행 완료를 통지한다.)

  • 프로그램의 주 실행 흐름이 멈추지 않고 다른 작업을 계속할 수 있다.

③ async / await 키워드를 사용한 비동기 방식

static void Main(string [] args)
{
    AwaitDownloadString(); //비동기 메서드 호출
    Console.ReadLine(); //사용자가 엔터를 누를 때까지 대기
}

private static async void AwaitDownloadString()
{
    WebClient wc = new WebClient(); //객체 생성
    string text = await wc.DownloadStringTaskAsync(" ... "); //비동기 작업 대기
    Console.WriteLine(text); //다운로드 완료 후 텍스트 출력
}
  • AwaitDownloadString()은 비동기 메서드이므로 즉시 반환된다. (메인 스레드는 멈추지 X)

  • wc.DownloadStringTaskAsync(" ... "); 네트워크에서 데이터를 다운로드 하는 비동기 작업을 수행한다.

  • await는 작업이 완료될 때까지 기다리지만 메인 스레드는 대기하지 않고 자유롭게 다른 작업을 수행할 수 있다.

  • 다운로드가 완료되면 Console.WriteLine(text)가 실행된다. 다운로드가 끝날 때까지 기다렸다가 텍스트를 출력한다.


0개의 댓글