[c#] async / await 사용 예시
C# async await
예제
// 3가지 주의해야할 Signiture가 있음:
// - 메소드는 async 수식어가 있어야한다.
// - return type은 Task나 Task<T>여야 한다.
// 여기서는, integer를 반환하기 때문에 Task<int>를 반환한다.
// - 메소드의 이름은 Async로 끝나야한다.
async Task<int> AccessTheWebAsync()
{
using (HttpClient client = new HttpClient())
{
// GetStringAsync는 Task<string>을 리턴.
// 기다리면 문자열을 얻는다는 뜻이다. (urlContents).
Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com");
// GetStringAsync로부터 얻는 string 값과 관계 없는 작업을 여기서 진행한다.
DoIndependentWork();
// await 연산자는 AccessTheWebAsync를 지연시킵니다.
// - getStringTask가 완료되기 전에 AccessTheWebAsync를 진행할 수 없습니다.
// - 그동안 제어권은 호출자인 AccessTheWebAsync로 넘어갑니다.
// - getStringTask가 완료되면 제어가 다시 시작됩니다.
// - await 연산자는 getStringTask로부터 문자열 결과를 가져옵니다.
string urlContents = await getStringTask;
// return 구문은 integer 결과를 명시합니다.
// AccessTheWebAsync를 기다리는 모든 메서드는 length 값을 가져옵니다.
return urlContents.Length;
}
}
async / await의 개념은 위의 예제를 통해 이해할 수 있습니다. Async한 작업을 하는 메서드 내에서는 다음과 같은 일을 하게 됩니다.
- Async 메서드 호출
- Async 작업과 관련 없는 작업 진행
- 완료를 기다림
만약 2번의 과정이 필요 없다면 다음과 같이 코드를 단순화 할 수 있습니다.
string urlContents = await client.GetStringAsync("https://docs.microsoft.com");
메서드는 일반적으로 비동기 작업이 완료될 때까지 메서드가 계속될 수 없는 지점을 표시하는 하나 이상의 대기 표현을 포함하고 있습니다. 한편, 메서드가 일시 중단되고 컨트롤이 메서드의 호출자로 반환됩니다. 이 항목의 다음 단원은 일시 중단 지점에서 발생하는 상황을 보여줍니다.
비동기 메서드에서 수행되는 작업
비동기 프로그래밍을 이해하는 데 있어 가장 중요한 점은 메서드에서 메서드로 제어 흐름을 이동하는 방법입니다. 다음 다이어그램이 과정을 안내합니다.
다이어그램의 번호는 다음의 순서로 진행됩니다.
이벤트 처리기가 호출되어
AccessTheWebAsync
비동기 메서드를 기다립니다.AccessTheWebAsync
는 HttpClient 인스턴스를 만들고 GetStringAsync 비동기 메서드를 호출하여 웹 사이트의 내용을 문자열로 다운로드합니다.GetStringAsync
에서 특정 작업이 발생하여 진행이 일시 중단됩니다. 웹 사이트에서 다운로드 또는 다른 차단 작업을 수행할 때까지 기다려야 할 수 있습니다. 리소스를 차단하지 않기 위해GetStringAsync
는 해당 호출자인AccessTheWebAsync
에 제어 권한을 양도합니다.GetStringAsync
는TResult
가 문자열인 Task를 반환하고AccessTheWebAsync
는getStringTask
변수에 작업을 할당합니다. 이 작업은 작업이 완료될 때 실제 문자열 값을 생성하기 위한 코드와 함께GetStringAsync
를 호출하는 지속적인 프로세스를 나타냅니다.getStringTask
가 아직 대기되지 않았으므로AccessTheWebAsync
가GetStringAsync
의 최종 결과에 무관한 다른 작업을 계속할 수 있습니다. 이 작업은 동기 메서드DoIndependentWork
를 호출하여 나타냅니다.DoIndependentWork
는 작업을 수행하고 호출자에게 반환하는 동기 메서드입니다.AccessTheWebAsync
에getStringTask
결과 없이 수행할 수 있는 작업이 없습니다. 다음으로AccessTheWebAsync
는 다운로드한 문자열의 길이를 계산하여 반환하려 하지만, 메서드가 문자열을 확인할 때까지 해당 값을 계산할 수 없습니다.따라서
AccessTheWebAsync
는 await 연산자를 사용해서 해당 프로세스를 일시 중단하고AccessTheWebAsync
를 호출한 메서드에 제어 권한을 양도합니다.AccessTheWebAsync
는Task<int>
를 호출자에게 반환합니다. 작업은 다운로드한 문자열의 길이인 정수 결과를 만든다는 약속을 나타냅니다.참고
GetStringAsync
가 대기하기 전에getStringTask
(및AccessTheWebAsync
)가 완료되면AccessTheWebAsync
에 컨트롤이 유지됩니다. 일시 중단 및AccessTheWebAsync
의 반환 비용은 호출된 비동기 프로세스(getStringTask
)가 이미 완료되었고AccessTheWebSync
가 최종 결과를 기다릴 필요가 없다면 낭비됩니다.호출자(이 예제의 이벤트 처리기) 내에서 처리 패턴이 계속됩니다. 호출자가 해당 결과를 기다리거나 즉시 기다리기 전에
AccessTheWebAsync
에서 결과에 의존하지 않는 다른 작업을 수행할 수 있습니다.AccessTheWebAsync
에 대한 이벤트가 대기 중이며AccessTheWebAsync
가GetStringAsync
를 대기 중입니다.GetStringAsync
가 완료되고 문자열 결과를 생성합니다.GetStringAsync
를 호출할 경우 문자열 결과가 예상대로 반환되지 않습니다. (메서드가 이미 3단계에서 작업을 반환했습니다.) 대신 문자열 결과가 메서드getStringTask
의 완료를 나타내는 작업에 저장됩니다. await 연산자가getStringTask
에서 결과를 검색합니다. 할당 문은 검색된 결과를urlContents
에 할당합니다.AccessTheWebAsync
에 문자열 결과가 있는 경우 메서드가 문자열 길이를 계산할 수 있습니다. 그런 다음AccessTheWebAsync
작업도 완료되고 대기 이벤트 처리기를 다시 시작할 수 있습니다. 이 항목 뒷부분의 전체 예에서는 이벤트 처리기가 길이 결과 값을 검색하고 출력하는지 확인할 수 있습니다. 비동기 프로그래밍을 처음 접하는 사용자인 경우 동기 동작과 비동기 동작의 차이점을 살펴보세요. 동기 메서드는 작업이 완료될 때 반환되지만(5단계) 비동기 메서드는 작업이 일시 중단될 때 반환됩니다. 비동기 메서드가 해당 작업을 완료하면 작업이 완료된 것으로 표시되고 결과가 있을 경우 작업에 저장됩니다.
스레드
비동기 메서드는 비차단 작업입니다. 비동기 메서드의 await
식은 대기한 작업이 실행되는 동안 현재 스레드를 차단하지 않습니다. 대신에 이 식은 메서드의 나머지를 연속으로 등록하고 제어 기능을 비동기 메서드 호출자에게 반환합니다.
async
및 await
키워드로 인해 추가 스레드가 생성되지 않습니다. 비동기 메서드는 자체 스레드에서 실행되지 않으므로 다중 스레드가 필요하지 않습니다. 메서드는 현재 동기화 컨텍스트에서 실행되고 메서드가 활성화된 경우에만 스레드에서 시간을 사용합니다. Task.Run을 사용하여 CPU 바인딩 작업을 백그라운드 스레드로 이동할 수 있지만 백그라운드 스레드는 프로세스를 지원하지 않고 결과를 사용할 수 있을 때까지 기다립니다.
비동기 프로그래밍에 대한 비동기 기반 접근 방법은 거의 모든 경우에 기존 방법보다 선호됩니다. 특히, 이 접근 방식은 코드가 더 간단하고 경합 조건을 방지할 필요가 없기 때문에 I/O 바인딩 작업의 BackgroundWorker 클래스보다 효과적입니다. 비동기 프로그래밍은 코드 실행에 대한 조합 세부 정보를 Task.Run
가 스레드 풀로 변환하는 작업과 구분하기 때문에 Task.Run 메서드를 함께 사용하는 비동기 프로그래밍은 CPU 바인딩 작업을 위한 BackgroundWorker보다 효과가 뛰어납니다.
전체 예제
다음 코드는 이 항목에서 설명하는 WPF(Windows Presentation Foundation) 애플리케이션의 MainWindow.xaml.cs 파일입니다. 비동기 샘플: "Async 및 Await를 사용하는 비동기 프로그래밍"의 예제에서 샘플을 다운로드할 수 있습니다.
C#복사
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// Add a using directive and a reference for System.Net.Http;
using System.Net.Http;
namespace AsyncFirstExample
{
public partial class MainWindow : Window
{
// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
// Call and await separately.
//Task<int> getLengthTask = AccessTheWebAsync();
//// You can do independent work here.
//int contentLength = await getLengthTask;
int contentLength = await AccessTheWebAsync();
resultsTextBox.Text +=
$"\r\nLength of the downloaded string: {contentLength}.\r\n";
}
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
using (HttpClient client = new HttpClient())
{
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
}
void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
}
}
// Sample Output:
// Working . . . . . . .
// Length of the downloaded string: 25035.