C#教科書を身につける.非同期プログラミング(async&await)

15896 ワード

1.非同期プログラミング(async&await)


https://youtu.be/PwoF8jsCUtM

01.同期と非同期

  • 同期
  • var data = DownloadData(...);
    ProcessData(data);
  • 非同期
  • var future = DownloadDataAsync(...);
    future.ContinewWith(data => ProcessData(data));

  • ディナーモデリング:同期プログラミング


  • ディナーモデリング:非同期プログラミング(同期を含む)


  • ディナーモデリング:非同期プログラミング


  • ディナースタイル

  • 2.プロジェクト

  • Cooking.cs
  • async Task<>
    Tを返す
  • 非同期宣言Task
  • await Task.Delay()
  • Thread.Sleep();の非同期バージョン
  • using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Dinner.Common
    {
        public class Cooking
        {
            // 동기 방식의 밥 만들기 메서드 
            // <returns></returns>
            public Rice MakeRice()
            {
                Console.WriteLine("밥 생성중...");
                Thread.Sleep(3001);
                return new Rice();
            }
    
            // 비동기 방식의 밥 만들기 메서드 
            // <returns></returns>
            public async Task<Rice> MakeRiceAsync()
            {
                Console.WriteLine("밥 생성중...");
                await Task.Delay(3001); //[A]
                return new Rice();
            }
    
            // 동기 방식의 국 만들기 메서드
            // <returns></returns>
            public Soup MakeSoup()
            {
                Console.WriteLine("국 생성중...");
                Thread.Sleep(3001);
                return new Soup();
            }
    
            // 비동기 방식의 국 만들기 메서드
            // <returns></returns>
            public async Task<Soup> MakeSoupAsync()
            {
                Console.WriteLine("국 생성중...");
                await Task.Run(() => Task.Delay(3001)); //[B]
                return new Soup();
            }
    
            // 동기 방식의 달걀 만들기 메서드
            // <returns>달걀</returns>
            public Egg MakeEgg()
            {
                Console.WriteLine("달걀 생성중...");
                Thread.Sleep(3001);
                return new Egg();
            }
    
            // 비동기 방식의 달걀 만들기 메서드
            // <returns>달걀</returns>
            public async Task<Egg> MakeEggAsync()
            {
                Console.WriteLine("달걀 생성중...");
                await Task.Delay(TimeSpan.FromMilliseconds(3001));
                return await Task.FromResult<Egg>(new Egg()); //[C]
            }
        }
    
        public class Rice
        {
            // Pass
        }
    
        public class Soup
        {
            // Pass
        }
    
        public class Egg
        {
            // Pass
        }
    }
  • Sync/Program.cs
  • using Dinner.Common;
    using System;
    using System.Diagnostics;
    
    namespace Dinner.Sync
    {
        class Program
        {
            static void Main(string[] args)
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
    
                //[1] 밥 만들기
                Rice rice = (new Cooking()).MakeRice(); // 스레드 차단: true
                Console.WriteLine($"밥 준비 완료 - {rice.GetHashCode()}");
    
                //[2] 국 만들기
                Soup soup = (new Cooking()).MakeSoup();
                Console.WriteLine($"국 준비 완료 - {soup.GetHashCode()}");
    
                //[3] 달걀 만들기
                Egg egg = (new Cooking()).MakeEgg();
                Console.WriteLine($"달걀 준비 완료 - {egg.GetHashCode()}");
    
                stopwatch.Stop();
    
                Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}밀리초");
                Console.WriteLine("동기 방식으로 식사 준비 완료");
            }
        }
    }
  • Async/Program.cs
  • 各方法が待機しているため、動機と時間は同じであるが、動作は非同期の
  • である.
    using Dinner.Common;
    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    
    namespace Dinner.Async
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
    
                Rice rice = await (new Cooking()).MakeRiceAsync(); // 스레드 차단: false
                Console.WriteLine($"밥 준비 완료: {rice.GetHashCode()}");
    
                Soup soup = await (new Cooking()).MakeSoupAsync();
                Console.WriteLine($"국 준비 완료: {soup.GetHashCode()}");
    
                Egg egg = await (new Cooking()).MakeEggAsync();
                Console.WriteLine($"달걀 준비 완료: {egg.GetHashCode()}");
    
                stopwatch.Stop();
    
                Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}밀리초");
                Console.WriteLine("비동기 방식으로 식사 준비 완료");
            }
        }
    }
  • Program.cs
  • await
  • Task<>
  • 3 Asyncメソッド並列実行
  • await Task.WhenAll()
  • すべてのタスクの完了を待つ
  • allTasks.Any()
  • タスクがある場合は、
  • を実行します.
    using System;
    using static System.Console;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using Dinner.Common;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Dinner
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                Console.WriteLine("[?] 어떤 방식으로 실행할까요? (0~4 번호 입력)\n" +
                    "0. 동기\t\t1. await\t2. Task<T>\t3. WhenAll\t4. WhenAny ");
                var number = Convert.ToInt32(Console.ReadLine());
    
                switch (number)
                {
                    case 1: // 비동기(동기 프로그램을 포함한)
                        {
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
    
                            Egg egg = await (new Cooking()).MakeEggAsync();
                            Console.WriteLine($"달걀 재료 준비 완료: {egg.GetHashCode()}");
    
                            Rice rice = await (new Cooking()).MakeRiceAsync();
                            Console.WriteLine($"김밥 준비 완료: {rice.GetHashCode()}");
    
                            Soup soup = await (new Cooking()).MakeSoupAsync();
                            Console.WriteLine($"국 준비 완료: {soup.GetHashCode()}");
    
                            stopwatch.Stop();
    
                            Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}");
                            Console.WriteLine("비동기 방식으로 식사(김밥) 준비 완료");
                        }
                        break;
                    case 2: // 비동기(함께 실행)
                        {
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
    
                            // 3개의 Async 메서드가 동시 실행
                            Task<Rice> riceTask = (new Cooking()).MakeRiceAsync();
                            Task<Soup> soupTask = (new Cooking()).MakeSoupAsync();
                            Task<Egg> eggTask = (new Cooking()).MakeEggAsync();
    
                            Rice rice = await riceTask;
                            Console.WriteLine($"식탁에 밥 준비 완료: {rice.GetHashCode()}");
                            Soup soup = await soupTask;
                            Console.WriteLine($"식탁에 국 준비 완료: {soup.GetHashCode()}");
                            Egg egg = await eggTask;
                            Console.WriteLine($"식탁에 달걀 준비 완료: {egg.GetHashCode()}");
    
                            stopwatch.Stop();
    
                            Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}");
                            Console.WriteLine("비동기 방식으로 식사 준비 완료");
                        }
                        break;
                    case 3: // 비동기(모두 완료되는 시점)
                        {
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
    
                            // 3개의 Async 메서드가 동시 실행
                            Task<Rice> riceTask = (new Cooking()).MakeRiceAsync();
                            Task<Soup> soupTask = (new Cooking()).MakeSoupAsync();
                            Task<Egg> eggTask = (new Cooking()).MakeEggAsync();
    
                            // 모든 작업이 다 완료될 때까지 대기
                            await Task.WhenAll(riceTask, soupTask, eggTask);
    
                            Console.WriteLine("식탁에 모든 식사 준비 완료");
    
                            stopwatch.Stop();
    
                            Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}");
                            Console.WriteLine("비동기 방식으로 식사 준비 완료");
                        }
                        break;
                    case 4:
                        {
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
    
                            // 3개의 Async 메서드가 동시 실행
                            Task<Rice> rTask = (new Cooking()).MakeRiceAsync();
                            Task<Soup> sTask = (new Cooking()).MakeSoupAsync();
                            Task<Egg> eTask = (new Cooking()).MakeEggAsync();
    
                            // 하나라도 작업이 끝나면 확인
                            var allTasks = new List<Task> { rTask, sTask, eTask };
                            while (allTasks.Any()) // 작업이 하나라도 있으면 실행
                            {
                                Task finished = await Task.WhenAny(allTasks);
                                if (finished == rTask)
                                {
                                    Rice rice = await rTask;
                                    Console.WriteLine($"밥 준비 완료 - {rice}");
                                }
                                else if (finished == sTask)
                                {
                                    Soup soup = await sTask;
                                    Console.WriteLine($"국 준비 완료 - {soup}");
                                }
                                else
                                {
                                    Egg egg = await eTask;
                                    Console.WriteLine($"달걀 준비 완료 - {egg}");
                                }
                                allTasks.Remove(finished); // 끝난 작업은 리스트에서 제거
                            }
    
                            stopwatch.Stop();
    
                            Console.WriteLine(
                                $"\n시간: {stopwatch.ElapsedMilliseconds}");
                            Console.WriteLine("비동기 방식으로 식사 준비 완료");
                        }
                        break;
                    default: // 동기(Sync)
                        {
                            Stopwatch stopwatch = new Stopwatch();
                            stopwatch.Start();
    
                            //[1] 밥 만들기
                            Rice rice = (new Cooking()).MakeRice(); // 스레드 차단: true
                            Console.WriteLine($"밥 준비 완료 - {rice.GetHashCode()}");
    
                            //[2] 국 만들기
                            Soup soup = (new Cooking()).MakeSoup();
                            Console.WriteLine($"국 준비 완료 - {soup.GetHashCode()}");
    
                            //[3] 달걀 만들기
                            Egg egg = (new Cooking()).MakeEgg();
                            Console.WriteLine($"달걀 준비 완료 - {egg.GetHashCode()}");
    
                            stopwatch.Stop();
    
                            Console.WriteLine($"\n시간: {stopwatch.ElapsedMilliseconds}");
                            Console.WriteLine("동기 방식으로 식사 준비 완료");
                        }
                        break;
                }
            }
        }
    }