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