C#

C# 비동기 프로그래밍: 비동기 vs 동기 처리

검은고양이개발자 2024. 7. 10. 14:30
반응형

이 글에서는 비동기와 동기 처리의 차이를 코드 예제를 통해 비교해 보겠습니다. 

 

동기 프로그래밍


동기 프로그래밍에서는 각 작업이 순차적으로 실행됩니다. 한 작업이 완료될 때까지 다음 작업이 시작되지 않습니다. 이러한 방식은 간단하지만, 여러 작업을 동시에 처리해야 할 때 비효율적일 수 있습니다.

동기 프로그래밍 예제

다음은 동기적으로 아침 식사를 준비하는 코드 예제입니다. 이 코드는 커피를 만들고, 계란을 부치고, 베이컨을 굽고, 토스트를 준비하는 순서로 실행됩니다.

 

C#

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace SyncExample
{
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            Coffee cup = MakeCoffee();
            Console.WriteLine("Coffee is ready");

            Egg eggs = FryEggs(2);
            Console.WriteLine("Eggs are ready");

            Bacon bacon = FryBacon(3);
            Console.WriteLine("Bacon is ready");

            Toast toast = MakeToastWithButterAndJam(2);
            Console.WriteLine("Toast is ready");

            stopwatch.Stop();
            Console.WriteLine($"Breakfast is ready! Total time: {stopwatch.ElapsedMilliseconds / 1000.0} seconds");
        }

        private static Coffee MakeCoffee()
        {
            Console.WriteLine("Pouring coffee");
            Task.Delay(1000).Wait();  // Simulate the coffee making time
            return new Coffee();
        }

        private static Egg FryEggs(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            Task.Delay(3000).Wait();  // Simulate warming the pan
            Console.WriteLine($"Cracking {howMany} eggs");
            Console.WriteLine("Cooking the eggs ...");
            Task.Delay(3000).Wait();  // Simulate cooking the eggs
            Console.WriteLine("Put eggs on plate");
            return new Egg();
        }

        private static Bacon FryBacon(int slices)
        {
            Console.WriteLine($"Putting {slices} slices of bacon in the pan");
            Console.WriteLine("Cooking first side of bacon...");
            Task.Delay(3000).Wait();  // Simulate cooking the first side
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Flipping a slice of bacon");
            }
            Console.WriteLine("Cooking the second side of bacon...");
            Task.Delay(3000).Wait();  // Simulate cooking the second side
            Console.WriteLine("Put bacon on plate");
            return new Bacon();
        }

        private static Toast MakeToastWithButterAndJam(int number)
        {
            var toast = ToastBread(number);
            ApplyButter(toast);
            ApplyJam(toast);
            return toast;
        }

        private static Toast ToastBread(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            Task.Delay(3000).Wait();  // Simulate toasting
            Console.WriteLine("Remove toast from toaster");
            return new Toast();
        }

        private static void ApplyButter(Toast toast)
        {
            Console.WriteLine("Putting butter on the toast");
            Task.Delay(1000).Wait();  // Simulate applying butter
        }

        private static void ApplyJam(Toast toast)
        {
            Console.WriteLine("Putting jam on the toast");
            Task.Delay(1000).Wait();  // Simulate applying jam
        }
    }
}

 

위 코드는 각 작업을 순차적으로 실행하여 전체 실행 시간이 모든 작업의 시간을 합한 것과 같습니다. 예를 들어, 커피(1초), 계란(6초), 베이컨(6초), 토스트(5초)라면 총 18초가 소요됩니다.

 

 

 

 

비동기 프로그래밍


비동기 프로그래밍에서는 여러 작업을 동시에 실행할 수 있습니다. 이렇게 하면 작업들이 서로 독립적으로 실행되어, 전체 실행 시간이 가장 오래 걸리는 작업의 시간으로 줄어듭니다.

 

비동기 프로그래밍 예제

다음은 비동기적으로 아침 식사를 준비하는 코드 예제입니다. 이 코드는 여러 작업을 동시에 실행하여 전체 실행 시간을 단축시킵니다.

 

C# 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

namespace AsyncExample
{
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static async Task Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            var coffeeTask = MakeCoffeeAsync();
            var eggsTask = FryEggsAsync(2);
            var baconTask = FryBaconAsync(3);
            var toastTask = MakeToastWithButterAndJamAsync();

            await Task.WhenAll(coffeeTask, eggsTask, baconTask, toastTask);

            stopwatch.Stop();
            Console.WriteLine($"Breakfast is ready! Total time: {stopwatch.ElapsedMilliseconds / 1000.0} seconds");
        }

        private static async Task<Coffee> MakeCoffeeAsync()
        {
            Console.WriteLine("Pouring coffee");
            await Task.Delay(1000);
            return new Coffee();
        }

        private static async Task<Egg> FryEggsAsync(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            await Task.Delay(3000);
            Console.WriteLine($"Cracking {howMany} eggs");
            Console.WriteLine("Cooking the eggs ...");
            await Task.Delay(3000);
            Console.WriteLine("Put eggs on plate");
            return new Egg();
        }

        private static async Task<Bacon> FryBaconAsync(int slices)
        {
            Console.WriteLine($"Putting {slices} slices of bacon in the pan");
            Console.WriteLine("Cooking first side of bacon...");
            await Task.Delay(3000);
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Flipping a slice of bacon");
            }
            Console.WriteLine("Cooking the second side of bacon...");
            await Task.Delay(3000);
            Console.WriteLine("Put bacon on plate");
            return new Bacon();
        }

        private static async Task<Toast> MakeToastWithButterAndJamAsync()
        {
            var toast = await ToastBreadAsync();
            await ApplyButterAsync(toast);
            await ApplyJamAsync(toast);
            return toast;
        }

        private static async Task<Toast> ToastBreadAsync()
        {
            for (int slice = 0; slice < 2; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            await Task.Delay(3000);
            Console.WriteLine("Remove toast from toaster");
            return new Toast();
        }

        private static async Task ApplyButterAsync(Toast toast)
        {
            Console.WriteLine("Putting butter on the toast");
            await Task.Delay(1000);
        }

        private static async Task ApplyJamAsync(Toast toast)
        {
            Console.WriteLine("Putting jam on the toast");
            await Task.Delay(1000);
        }
    }
}

 

위 비동기 코드는 여러 작업을 동시에 시작하고 Task.WhenAll을 사용하여 모든 작업이 완료될 때까지 기다립니다. 각 작업이 동시에 실행되기 때문에, 전체 실행 시간은 가장 오래 걸리는 작업의 시간(예: 6초)이 됩니다.

 

 

동기 vs 비동기 실행 시간 비교


동기적으로 실행했을 때는 각 작업이 순차적으로 실행되므로 전체 실행 시간이 모든 작업의 실행 시간을 합한 값이 됩니다. 반면에, 비동기적으로 실행했을 때는 여러 작업이 동시에 실행되므로 전체 실행 시간이 가장 오래 걸리는 작업의 시간으로 줄어듭니다.

 

작업 소요 시간

커피 1초
계란 6초
베이컨 6초
토스트 5초
동기 총 시간 18초
비동기 총 시간 6초

 

결론

 

비동기 프로그래밍은 특히 여러 작업을 동시에 처리해야 할 때 매우 유용합니다.  비동기 방식은 전체 실행 시간을 크게 줄일 수 있음을 확인할 수 있습니다. 

 

참고 : https://learn.microsoft.com/ko-kr/dotnet/csharp/asynchronous-programming/

 

C#의 비동기 프로그래밍 - C#

async, await 및 Task를 사용하여 비동기 프로그래밍을 지원하는 C# 언어에 대해 간략히 설명합니다.

learn.microsoft.com

 

반응형