본문 바로가기
c#

[c#] BeginInvoke 사용법 및 예제 -4- (BeginInvoke 예제)

by devjh 2020. 10. 2.
반응형

개요

1. Thread

2. lock

3. BeginInvoke(비동기, 스레드풀)

4. BeginInvoke2(비동기 리턴값)

5. BeginInvoke3(콜백)

6. BeginInvoke4(BeginInvoke 예제)

 

 

1. 예제

BeginInvoke의 종합 예제를 만들어봤습니다.

 

BeginInvoke 1, 2, 3 편에서 나온것들을 정리하는 느낌으로 읽어보시면 좋을것 같습니다.

 

namespace CallbackEx
{
    class Program
    {
        static void Main(string[] args)
        {
            Boss boss = new Boss();
            boss.Run();
        }
    }
}

 

namespace CallbackEx
{
    public delegate int myDel(int n);
    class Boss
    {
        public void Run()
        {
            int goldCnt = 10;
            Console.WriteLine("보스 :        {0}개의 금을 캐오세요", goldCnt);
            myDel myWork = MyFunc;

            // 첫번째 매개변수는 대리자 메서드의 매개변수, 두번째 매개변수는 콜백메서드
            // 세번째매개변수는 콜백메서드에 들어갈 object형식의 아규먼트입니다.
            // 지금을 예제를 위해 단순히 문자열을 넘겨줬지만
            // 지난번 Thread1편의 여러개의 매개변수를 전달하는 방법처럼 클래스의 객체를 넘겨줄 수 있습니다.
            IAsyncResult myResult = myWork.BeginInvoke(goldCnt, new AsyncCallback(myCallback), "이제 좀만 쉴게요");
            
            // 외부에서 해당 비동기작업이 끝났는지 확인하는 IsCompleted가 있습니다.
            // 해당구문처럼 while안에 넣어서 메인스레드 진행을 강제로 대기시킬수 있습니다.
            while (!myResult.IsCompleted)
            {
                Console.WriteLine("보스 :        커피마시는중~");
                Thread.Sleep(1000);
            }


            // 아래구문은 정상적으로 블락시키는 방법입니다.
            // 위의 while문이 없다면 해당 스레드는 비동기구문이 끝날때까지 여기서 대기합니다.
            // msdn에서 권장하는 블락방법이니 한번 넣어봤습니다.
            myResult.AsyncWaitHandle.WaitOne();

          
            int goldResult = myWork.EndInvoke(myResult);

            Console.WriteLine("{0}개의 금을 다 캤구나!", goldResult);
            
            // 비동기작업이 끝나고 리턴값을 받으면 아래의 포문이 돌아갑니다.
            // 콜백메서드가 끝나는 시점이 아니라는점에 주의합셔야 됩니다.
            // 비동기작업이 끝나고 콜백메서드로 넘어가면서 같이 실행됩니다.
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("보스 :        다캔것같은데 뭐하고있니?");
                Thread.Sleep(1000);
            }
        }

        private void myCallback(IAsyncResult ar)
        {
            Thread.Sleep(1000);
            // 비동기작업이 끝나면 실행되는 콜백메서드입니다.
            // 메인문은 비동기작업이 끝난순간 WaitOne과 EndInvoke의 블락을 풀어버리며 실행됩니다.
            // 콜백메서드가 끝날때까지 메인문이 블락되지는 않는다는점을 조심하셔야 됩니다.

            // 맨처음 비동기작업을 지시할때 넘겨준 객체는 아래의 방법으로 제어합니다.
            // 저는 간단한 예제를 위해 문자열인 "이제 좀만 쉴게요" 를 넘겼습니다.
            Console.WriteLine("작업자 :      {0}",ar.AsyncState);
            
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("작업자 :      쉬는중입니다.");
                Thread.Sleep(300);
            }
        }

        public int MyFunc(int n)
        {
            int sum = 0;
            for (int i = 0; i < n; i++)
            {
                Console.WriteLine("작업자 :      {0}개의 금을 캤습니다.", i+1);
                sum++;
                Thread.Sleep(300);
            }
            return sum;
        }
    }
}

 

2. 실행결과

 

이번코드의 로직입니다.

 

보스 스레드(메인스레드):

1. 보스는 비동기로 작업자에게 금을 캐올것을 비동기로 지시합니다.

 

2. 그리고 커피를 먹습니다.(이번에는 스레드가 죽을때까지 무한루프를 돌려서 호출한 스레드를 블락시켜봤습니다. 비동기작업도 스레드풀에서 돌아가는 스레드이므로 해당방법으로 블락이 가능합니다. 물론 이상적인 블락방법은 아닙니다. msdn에서 권장하는 블락방법은 WaitOne()을 통한 블락입니다.)

 

3. 금을 캐오라는 비동기작업이 다끝나면 보스는 블락되있던 메인스레드의 블락이 풀리고(WaitOne이 set되어 싱글상태가 됩니다.)

 

4. 캔 금의 수를 확인합니다.(EndInvoke)

 

5. 콜백메서드가 실행과 함께 뭐하냐고 3번 물어보고 작업이 종료됩니다.

 

작업자 스레드:

1. 금 5개를 캐오라고 지시받으면 그만큼 for문을 돌며 금을 캤다고 보고합니다.

 

2. 다 캐면 값을 return해주고(WaitOne을 Set시켜 메인스레드의 블락을 풀며, EndInvoke를 통해 리턴을 받게해줍니다)

지정해놓은 콜백메서드(새로운 스레드풀의 스레드로 돌아갑니다.)가 실행됩니다.

 

3. 콜백메서드에서 쉬고있다고 말합니다.

 


 

지금까지 BeginInvoke의 사용법에 대해 알아보았습니다.

 

이전게시글에서도 언급했지만 주의할점 두가지를 꼭 기억해야합니다.

 

1. EndInvoke는 콜백메서드나 호출한 스레드에서 한번만 호출할 수 있습니다.

2. 메인문의 블락이 풀리는 시점은 콜백메서드가 끝나는 순간이 아닌, 비동기작업이 끝나는 순간입니다.

 

실제 콜백은 보통 외부시스템에서 지시한 작업(소켓읽기작업 등)이 끝난순간 실행되는 경우가 많습니다.

또한 비동기작업에는 리턴값과 매개변수의 갯수를 미리 지정해서 delegate와 캡슐화 시켜놓은

 

Action과 Func키워드가 있습니다.

 

해당 예악어들을 사용하면 delegate 선언을 하지 않아도 된다는 장점이 있으니

 

delegate의 매개변수를 명시적으로 확인하지 않아도 된다면 사용하는것도 좋습니다.

 

마지막으로 닷넷4.0버전 이상에서는 Task키워드를 사용할 수 있습니다.

 

Task는 async await키워드를 통해 호출권 제어방법을 강화시켰고 

 

TaskCompletionSource나 ContinueWith를 통해

 

스레드안정성과 동기화, 콜백이 강화되었으니

 

해당 키워드들을 추가로 공부해보시면 c# 스레드 및 비동기작업의 주요기능을 모두 확인하실 수 있습니다.

 

thread 끝.

반응형

댓글