728x90

C# 5.0

C# async는 컴파일러에게 해당 메서드가 await를 가지고 있음을 알려주는 역활을하고,

async라고 표시된 메서드는 await를 1개 이상 가질 수 있다.

 

await는 일반적으로 Task 혹은 Task<T> 객체와 함께 사용된다.

Task 이외의 클래스도 사용 가능한데,

awaitable 클래스, 즉 GetAwaiter() 라는 메서드를 갖는 클래스이면 함께 사용 가능하다.

 

await는 해당 Task가 끝날 때까지 기다렸다가 완료 후, await 바로 다음 실행문부터 실행을 계속한다.

await가 기다리는 Task 혹은 실행 메서드는 별도의 Worker Thread에서 돌 수도 있고, 또는 UI Thread에서 돌 수도 있다

 

Run()이라는 async 메서드를 실행하고,

Run 메서드 안에서 비동기 Task를 만들어 실행하고 결과를 기다리는 await 문의 예시

// 예제1
private void button1_Click(object sender, EventArgs e)
{
     Run();  //UI Thread에서 실행
}

private async void Run()
{
    // 비동기로 Worker Thread에서 도는 task1
    // Task.Run(): .NET Framework 4.5+
    var task1 = Task.Run(() => LongCalcAsync(10));

    // task1이 끝나길 기다렸다가 끝나면 결과치를 sum에 할당
    int sum = await task1;

    // UI Thread 에서 실행
    // Control.Invoke 혹은 Control.BeginInvok 필요없음
    this.label1.Text = "Sum = " + sum;
    this.button1.Enabled = true;
}

private int LongCalcAsync(int times)
{
    int result = 0;
    for (int i = 0; i < times; i++)
    {
        result += i;
        Thread.Sleep(1000); 
    }
    return result;
}

.NET 4.5는 기존의 동기화(Synchronous) 메서드들과 구분하여

C#의 await (혹은 VB의 Await)를 지원하기 위해 많은 Async 메서드들을 추가함

System.IO.Stream.Read() : 기존 동기 메서드
System.IO.Stream.ReadAsync() : 4.5 Async 메서드

WebClient.DownloadStringAsync() : 기존 비동기 메서드
WebClient.DownloadStringTaskAsync() : 4.5 TaskAsync 메서드

예시

 public void Run()
    {
        //데이터 Read를 비동기로 호출!
        AwaitRead();
        //호출 되고 있는동안 Main에서는 1ms로 숫자를 표시
        for (int i = 0; i < 30; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(1);
        }
    }
    private async void AwaitRead()
    {
        using (FileStream fs = new FileStream(@"./YangbengHis.xml", FileMode.Open, FileAccess.Read))
        {
            byte[] buffer = new byte[fs.Length];
            //awit 호출하기 전에 Thread ID와
            Console.WriteLine($"Before ReadAsync : {Thread.CurrentThread.ManagedThreadId}");

            await fs.ReadAsync(buffer, 0, buffer.Length);
            //awit 호출 후 Thread의 ID 가 다르다.!!!
            Console.WriteLine($"Before ReadAsync : {Thread.CurrentThread.ManagedThreadId}");
            //아래의 두 줄의 내용은 readAsysnc가 끝난 이후 비동기 호출이 된다.
            string txt = Encoding.UTF8.GetString(buffer);
            Console.WriteLine(txt);
        }
    }

await : UI 쓰레드에서 도는 Task

// 예제3
private void button1_Click(object sender, EventArgs e)
{
     Run();  //UI Thread에서 실행
}

private async void Run()
{    
    int sum = await LongCalc2(10);
    this.label1.Text = "Sum = " + sum;
    this.button1.Enabled = true;
}

private async Task<int> LongCalc2(int times)
{
    //UI Thread에서 실행
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    int result = 0;
    for (int i = 0; i < times; i++)
    {
        result += i;                
        await Task.Delay(1000);
    }
    return result;
}

await : Task.ContinueWith()

// 예제4
private void Run2()
{    
    var task1 = Task<int>.Run(() => LongCalc2(10));
    
    // await task1과 동일한 효과
    //
    task1.ContinueWith(x => {
      this.label1.Text = "Sum = " + task1.Result;
      this.button1.Enabled = true;      
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

콘솔프로그램에서의 await

awiat 실행 이전에 SynchronizationContext.Current 가 null 임을 체크하고 있으며,

또한 await 이후에 쓰레드가 작업 쓰레드임을 ManagedThreadId 속성을 통해 확인

// 예제5
//using System;
//using System.Threading;
//using System.Threading.Tasks;

static void Main(string[] args)
{            
    Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    Run();
    Console.ReadLine();
}

private async void Run()
{
    // 비동기로 Worker Thread에서 도는 task1
    var task1 = Task.Run(() => LongCalcAsync(10));

    // 콘솔프로그램인 경우 SynchronizationContext가 null
    Console.WriteLine(SynchronizationContext.Current);

    // task1이 끝나길 기다렸다가 끝나면 결과치를 sum에 할당
    int sum = await task1;

    // Worker Thread 에서 실행
    Console.WriteLine(sum);
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

private int LongCalcAsync(int times)
{
    int result = 0;
    for (int i = 0; i < times; i++)
    {
        result += i;
        Thread.Sleep(1000); 
    }
    return result;
}
728x90

+ Recent posts