C#/기초

[C#] 비도도돋도동기

아침밥챙겨먹어 2023. 12. 30. 23:32

동기(synchronous)

- 순차적으로 작업을 수행함

해당 작업이 수행중일 경우 다음 작업은 대기하게 됨, 요청을 대기시킨다.

간단하고 직관적이지만 결과가 주어질 때까지 아무것도 못하죠

 

비동기(Asynchronous)

- 요청이 들어오면 해당 요청에 의한 작업이 끝나지 않았더라도 계속 요청을 받는다.

자원을 비교적 효율저긍로 사용할수 있는 장점이 있음

 

1. Task

Task는 ThreadPool안에서 움직이는 Thread이다.

Thread처럼 쉽게 생성하고 Join기능도 있다.

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

namespace Test
{
    //쓰레드로 넘기는 파라미터 클래스
    class Node
    {
        public string Text { get; set; }
        public int Count { get; set; }

        public int Tick { get; set; }

    }
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMinThreads(0, 0);
            ThreadPool.SetMaxThreads(2, 0);

            var list = new List<Task<int>>();

            var func = new Func<object, int>((x) =>
            {
                var node = (Node)x;

                int sum = 0;

                for (int i = 0; i <= node.Count; i++)
                {
                    sum += i;

                    Console.WriteLine(node.Text + " = " + i);

                    Thread.Sleep(node.Tick);
                }
                Console.WriteLine("Completed " + node.Text);
                return sum;
            });

            list.Add(new Task<int>(func, new Node { Text = "A", Count = 5, Tick = 1000 }));
            list.Add(new Task<int>(func, new Node { Text = "B", Count = 5, Tick = 10 }));
            list.Add(new Task<int>(func, new Node { Text = "C", Count = 5, Tick = 500 }));
            list.Add(new Task<int>(func, new Node { Text = "D", Count = 5, Tick = 300 }));
            list.Add(new Task<int>(func, new Node { Text = "E", Count = 5, Tick = 200 }));

            list.ForEach(x => x.Start());

            list.ForEach(x => x.Wait());

            Console.WriteLine("Sum = " + list.Sum(x => x.Result));

            Console.WriteLine("End");
            Console.ReadLine();
        }
    }
}

 

결과 사진 

ThreadPool과 다르게 return값을 받아서 총합 결과가 나올수 있음

lock을 사용하지 않아도 각 쓰레드에서 결과값을 받아서 각 쓰레드의 값을 받아서 사용할 수 있다.

 


 

2. Async

 

void로 사용하는 방법과 Task클래스와 사용하는 방법으로 나누어져 있다.

 

하지만 만약 async를 void로 사용하게 될경우 그냥 비동기이다.

ThreadPool로 만드는것과 차이가 없다.

 

 

그래서 async의 리턴값은 Task로 사용하는게 맞다 라고 함

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

namespace Test
{
    class Program
    {

        static async Task<int> AsyncTest()
        {
            var task = new Task<int>(() =>
            {
                int sum = 0;
                for (int i = 0; i < 10; i++)
                {
                    sum += i;
                    Thread.Sleep(100);

                }
                return sum;
            });
            task.Start();
            await task;

            Console.WriteLine(task.Result);
            return 10;
        }

        static void Main(string[] args)
        {
            var task = AsyncTest();
            Console.WriteLine("pass await 1");

            task.Wait();
            Console.WriteLine("pass await 2");

            int result = task.Result;

            Console.WriteLine(result);

            Console.WriteLine("END");
            Console.ReadKey();
        }
    }

}

 

결과사진

Main에서 AsyncTest 호출 -> AsyncTest메서드 안에서 task가 비동기적으로 실행 -> Wait함수에 의해 task가 await됨.


3. ContinueWith

 

ContinueWith 함수는 Task를 연달아 붙여서 사용할때 사용함

C#의 콜백함수라고 하는데 왜그런진 모르겠음 

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

namespace Test
{
    class Program
    {

        static async Task<int> AsyncTest()
        {
            var task = new Task<int>(() =>
            {
                int sum = 0;
                for (int i = 0; i < 10; i++)
                {
                    sum += i;
                    Console.WriteLine(i);
                    Thread.Sleep(100);

                }
                return sum;
            });

            task.Start();
            await task;

            task = new Task<int>(() =>
            {
                int sum = 0;
                for(int i = 10; i < 20; i++)
                {
                    sum += i;
                    Console.WriteLine(i);
                    Thread.Sleep(100);
                }
                return sum;
            });
            task.Start();

            return task.Result;
        }

        static void Main(string[] args)
        {
            var continueTask = AsyncTest().ContinueWith(task =>
            {
                return task.Result;
            });
            Console.WriteLine(continueTask.Result); 
           
            Console.WriteLine("END");
            Console.ReadKey();
        }
    }

}

 

솔직히 왜써야하는지 잘 모르겠음

 

구지 ContinueWith를 쓰지않아도 저런식이면 알아서 다음 task가 실행될텐데 뭐지