반응형

 

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        //async/await : 비동기인데 멀티스레드가 아닌 경우가 있다  : coroutine 와 유사하다

        //task 는 일감을 말함
        static Task test()
        {
            Console.WriteLine("start test");

            //3초 후에 task 를 반환한다
            Task t = Task.Delay(3000);
            return t;
        }

        static void Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            Task t = test();

            Console.WriteLine("while start");
            while(true)
            {
                
            }
        }
    }
}

async/await : 비동기인데 멀티스레드가 아닌 경우가 있다  : coroutine 와 유사하다

 

Task 를 생성하고 delay 에 시간을 넣으면 생성과 동시에 시간이 흘러간다는 것을 알 수 있다

 

start test 문자가 뜨고 시간 지연 없이 바로 while start 가 호출되는데

        static Task test()
        {
            Console.WriteLine("start test");

            //3초 후에 task 를 반환한다
            Task t = Task.Delay(3000);
            return t;
        }

        static void Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            Task t = test();

            //처리 지점
              
            t.Wait();
            Console.WriteLine("while start");
            while(true)
            {
                
            }
        }

wait 이 들어가면 start test 가 찍힌 후 3초 이후에 while start 가 호출 되는것을 알 수 있다

 

그래서 //처리 지점    에서 task 가 완료 되는 동안 다른 동작을 할 수 있게 된다

 

 

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async void testAsync()
        {
            Console.WriteLine("start testAsync");

            //3초 후에 task 를 반환한다
            Task t = Task.Delay(3000);

            await t;
            Console.WriteLine("end testAsync");
        }

        static void Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            testAsync();

            Console.WriteLine("while start");
            while(true)
            {
                
            }
        }
    }
}

 

이 코드는 async/await 인데 결과는 다음과 같다

async 함수가 호출되고 task 가 완료 될때까지 await 으로 기다려지긴 하지만 제어권이 main 으로 넘어가서 처리할 것을 먼저 처리 할수 있는 형태가 된다 typescript 에 이와 유사한 문법들이 있다

 

또는 다음 처럼 간략한 버전으로 사용 할 수도 있다

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async void testAsync()
        {
            Console.WriteLine("start testAsync");

            //3초 후에 task 를 반환한다
            await Task.Delay(3000);
            Console.WriteLine("end testAsync");
        }

        static void Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            testAsync();

            Console.WriteLine("while start");
            while(true)
            {
                
            }
        }
    }
}

 

 

main 에서 testAsync 함수가 완료 될때까지 대기 시킬 수도 있다 다음 처럼 대기 할수 있고

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async Task testAsync()
        {
            Console.WriteLine("start testAsync");

            //3초 후에 task 를 반환한다
            await Task.Delay(3000);
            Console.WriteLine("end testAsync");
        }

        static async Task Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            await testAsync();

            Console.WriteLine("while start");
            while(true)
            {
                
            }
        }
    }
}

testAsync 함수가 모두 완료 될때까지 awiat testAsync(); 구문에서 대기하다가 완료 된 이후 main 의 나머지 구문을 이어 나간다

결과는 다음과 같다

 

 

값을 리턴 받고 싶다면 Task<T> 를 리턴하면 된다

 

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async Task<int> testAsync()
        {
            Console.WriteLine("start testAsync");

            //3초 후에 task 를 반환한다
            await Task.Delay(3000);
            Console.WriteLine("end testAsync");

            return 10;
        }

        static async Task Main(string[] args)
        {
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            int retValue = await testAsync();

            Console.WriteLine("while start " + retValue);
            while(true)
            {
                
            }
        }
    }
}

 

 

 

다음처럼 main 에서 다른일을 하기위해 task 를 별도의 변수로 두어 위 async 함수를 분리하여 동시에 처리할 일을 처리 할 수도 있다

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async Task<int> testAsync()
        {
            Console.WriteLine("start testAsync");

            //3초 후에 task 를 반환한다
            await Task.Delay(3000);
            Console.WriteLine("end testAsync");

            return 10;
        }

        static async Task Main(string[] args)
        {
            Task<int> t = testAsync();
            Console.WriteLine("다른 일");
            //위에서 task 를 만듬과 동시에 시간은 흐르게 된다
            int retValue = await t;

            Console.WriteLine("while start " + retValue);
            while(true)
            {
                
            }
        }
    }
}

 

 

 

예시 다음 처럼 조식을 만들때

다음 처럼 일반적인 방식인 순차적으로 생각 할 수도 있지만

using System;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static void Main(string[] args)
        {
            Coffee cup = PourCoffee();
            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 = ToastBread(2);
            ApplyButter(toast);
            ApplyJam(toast);
            Console.WriteLine("toast is ready");

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the 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();
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        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();
            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();
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static Egg FryEggs(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            Task.Delay(3000).Wait();
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Put eggs on plate");

            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
}

 

 

async 함수를 적절히 await 하여 처리 하여 대기 시간을 줄일 수 있다

그림은 위에서 아래로 내려가는 방향의 시간의 흐름이고 옆은 비동기로 실행 되는 개수라 보면 된다

 

 

 

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

Task<Egg> eggsTask = FryEggsAsync(2);
Task<Bacon> baconTask = FryBaconAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);

Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");

Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");

Console.WriteLine("Breakfast is ready!");

 

 

최종 방식은 List 로 Task 를 담아 대기 하는 방식으로 최종 15 분 안에 끝나는 예시이다

 

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

namespace AsyncBreakfast
{
    // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static async Task Main(string[] args)
        {
            Coffee cup = PourCoffee();
            Console.WriteLine("coffee is ready");

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

            var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
            while (breakfastTasks.Count > 0)
            {
                Task finishedTask = await Task.WhenAny(breakfastTasks);
                if (finishedTask == eggsTask)
                {
                    Console.WriteLine("eggs are ready");
                }
                else if (finishedTask == baconTask)
                {
                    Console.WriteLine("bacon is ready");
                }
                else if (finishedTask == toastTask)
                {
                    Console.WriteLine("toast is ready");
                }
                await finishedTask;
                breakfastTasks.Remove(finishedTask);
            }

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");
        }

        static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
        {
            var toast = await ToastBreadAsync(number);
            ApplyButter(toast);
            ApplyJam(toast);

            return toast;
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static async Task<Toast> ToastBreadAsync(int slices)
        {
            for (int slice = 0; slice < slices; 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<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<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 Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
}

 

ref : https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/

 

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

linq (2)  (0) 2023.04.09
Linq (1)  (0) 2023.04.08
C# - ArraySegment  (0) 2023.01.04
ThreadLocal<T> 와 AsyncLocal<T> 의 차이점  (0) 2022.12.29
Common Memory Leaks In C#  (0) 2022.12.04

+ Recent posts