본문 바로가기
C# & .NetCore

C# async / await

by 개발자 포비 2025. 8. 8.

 async와 await는 C#에서 비동기 프로그래밍을 구현하기 위한 키워드로, async는 메서드가 비동기 작업을 포함할 수 있음을 나타내며 Task나 Task<T>를 반환하도록 한다. 이 메서드 내부에서 await 키워드를 사용하면, 지정된 비동기 작업(Task)이 완료될 때까지 현재 메서드의 실행 흐름을 일시 중단하고 제어권을 반환하게 된다. 이때 흐름은 스레드를 차단하지 않고 대기하며, 작업이 완료되면 중단된 지점부터 실행을 재개한다. 즉, async는 비동기 흐름을 정의하고 await는 그 흐름 중 Task의 완료를 기다리는 구조를 만들어, 효율적인 비동기 처리를 가능하게 한다.

namespace Program
{
    class Program
    {
        // async 메서드는 Task를 반환해야 한다.
        // Task는 비동기 작업을 나타내는 클래스이며, 기본적으로 쓰레드 풀에서 실행된다.
        public static async Task TestAsync()
        {
            // async 키워드를 사용하면 해당 메서드를 비동기로 동작하도록 만든다.
            // async 키워드를 사용하더라도, 코드 블럭 내에 await가 존재하지 않는다면, 동기적으로 동작한다.

            Console.WriteLine("Test Async 작업 시작");

            var task = AsyncWork(); // 비동기 작업을 시작한다.

            Console.WriteLine("비동기 요청을 한 이후에 내 작업을 마저 한다.");

            // 작업 수행을 Thread.Sleep으로 대체
            // Task.Delay가 아닌 Thread.Sleep을 사용하면, 현재 스레드를 블로킹하게 된다.
            Thread.Sleep(500);

            // 비동기 작업을 수행하는 중, 다른 비동기 작업 수행을 대기해야 하는 경우가 발생한다. (ex. 비동기 호출의 결과가 필요)
            // 이러한 경우엔 await 키워드를 사용하여 비동기 작업을 기다린다.
            // await 키워드를 만나게 되는 경우, TestAsync 메서드는 제어권을 반환하고, 비동기 작업이 완료될 때까지 기다린다.
            var result = await task;


            // 비동기 작업이 완료된 이후 나머지 작업을 진행한다.
            Console.WriteLine($"Result : {result}");     
        }

        public static async Task<string> AsyncWork()
        {
            Console.WriteLine("Async Work 작업 시작");

            // 비동기 작업을 수행하는 메서드
            await Task.Delay(2000);     // 2초 동안 대기 (비동기 작업)
            Console.WriteLine("Async Work 작업 완료");

            return "작업 완료";         // 결과 반환
        }

        private static async Task Main()
        {
            // 아래의 두 코드는 동일한 결과를 출력한다.
            // TestAsync 메서드는 비동기 작업을 수행하고, 이 때 Task를 사용한다.
            // Main 메서드가 종료되는 시점에 백그라운드 스레드들은 자동으로 종료가 되므로, AsyncWork()가 정상적으로 작동하지 못한다.
            TestAsync();

            Thread thread = new Thread(async () =>
            {
                await TestAsync();
            });
            thread.Start();

            // Join을 사용하여도 동작하지 않는다.
            // 비동기 메서드를 실행하는 스레드는 Task를 곧바로 반환하게 되고, 이 것이 의도된 동작과 달리 Join에서 대가하지 않는다.
            // thread2.Join()은 thread2가 완료될때까지 기다리는 것이다.
            // thread2가 비동기 메서드를 호출한 후에 바로 반환되기 때문에, TestAsync()가 완료되기 전에 Main 메서드가 종료될 수 있다.
            Thread thread2 = new Thread(async () =>
            {
                await TestAsync();
            });
            thread2.Start();
            thread2.Join();

            // 따라서 Main 메서드가 종료되기 전에 TestAsync()가 완료될 수 있도록 기다려야 한다.
            await TestAsync(); 

			// * 각 호출들을 하나만 남기고 다른 코드는 주석처리하여 테스트하기 
        }
    }

댓글