본문 바로가기
c#

[c#] BeginInvoke사용법 및 예제 -3-(콜백)

by devjh 2020. 9. 7.
반응형

개요

1. Thread

2. lock

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

4. BeginInvoke2(비동기 리턴값)

5. BeginInvoke3(콜백)

6. BeginInvoke4(BeginInvoke예제)

 


 

이번게시글에서는 비동기작업의 콜백에 대해 알아보겠습니다.

 

콜백이란 피호출자가 호출자의 메서드를 실행해주는 것을 말하며

 

BeginInvoke에서의 콜백메서드는 비동기작업이 끝난 직후 실행됩니다.

(보통 비동기작업은 외부에서 끝내주는 경우가 많아 콜백이라고 합니다.)

 

즉 비동기호출을 한 스레드는 비동기의 일이 진행이 되던 말던 신경을 안쓰고 다음줄을 실행하게 되는데

 

작업이 끝났다는걸 콜백메서드를 통해 보고받고 원하는 로직을 실행한다고 생각하시면 됩니다.

 

비동기호출을 한 스레드를 블락시키거나, 스레드를 하나 더 만들어서 변수를 폴링시켜

 

비슷한 동작을 하게 할 수는 있으나

 

콜백은 비동기호출을 실행한 스레드가 살아있든, 죽어있든 호출되어

 

호출한 스레드를 따로 블락시킬 필요가 없으며

 

비동기호출의 IAsyncResult 및 매개변수를 받아와 작업을 할 수 있는 등 장점이 많습니다.

 

사용법을 예제코드를 통해 확인해보겠습니다.

 

1. 예제

namespace BeginInvokeTest3
{
    delegate int myDelegate(int n);
    class Program
    {
        static void Main(string[] args)
        {
            myDelegate a = Func;
            int cnt = 10;
            Console.WriteLine("1부터 {0}까지 더하고 다 더하면 결과를 말해주세요",cnt);

            // 첫번째 매개변수는 비동기작업에 전달하는 매개변수
            // 두번째 매개변수는 콜백메서드
            // 세번째 매개변수는 콜백메서드에 전달할 object형식의 매개변수이며
            // 콜백메서드에서 ar.AsyncState로 받아 올 수 있습니다.
            // 지난번 Thread1편의 여러개의 매개변수를 전달하는 방법처럼 
            // 객체를 넘겨주는 경우가 많습니다.

            IAsyncResult ar = a.BeginInvoke(cnt, new AsyncCallback(myCallback),cnt);

            Console.WriteLine("더할때까지 기다리고 있겠습니다.");
            // 이번게시글에서는 리턴값을 호출한 스레드에서 받아오지않으므로
            // WaitOne을 사용해서 메인스레드를 블락시켜줘야합니다.
            // 리턴값이 있다면 EndInvoke를 통해 리턴값이 나올때까지 블락시킬수도 있지만
            // msdn에서는 리턴값이 있든 없든
            // AsyncWaitHandle.WaitOne()을 사용하여 블락시킬것을 권장합니다.
            ar.AsyncWaitHandle.WaitOne();

            Console.WriteLine("다 더했다면 결과를 알려주세요");
            // 콜백메서드가 실행될수 있게 1초를 기다려준후 프로그램이 종료됩니다.
            Thread.Sleep(1000);
        }

        // 비동기 작업이 완료된 후 콜백메서드가 실행됩니다.
        private static void myCallback(IAsyncResult ar)
        {
            // EndInvoke를 사용하면 비동기 구문의 리턴값을 받아올 수 있습니다.
            myDelegate temp = ((AsyncResult)ar).AsyncDelegate as myDelegate;
            int result = temp.EndInvoke(ar);
            Console.WriteLine("1부터 {0}까지의 합은 {1}입니다.", ar.AsyncState, result);
        }

        private static int Func(int n)
        {
            int sum = 0;
            for (int i = 1; i <= n; i++)
            {
                sum += i;
                Thread.Sleep(100);
            }
            Console.WriteLine(sum);
            return sum;
        }
    }
}

 

 

저번게시글과 비슷하게 비동기작업으로 1부터 n까지의 합을 구하고 있습니다.

 

그러나 이번에는 콜백메서드에서 리턴값을 받아오도록 만들었습니다.

 

메인스레드는 비동기작업을 지시하고 WaitOne에서 작업이 끝날때까지 블락된 후

 

작업이 끝나면 WaitOne이 싱글상태가 되어 "다 더했다면 결과를 알려주세요"를 출력합니다.

 

비동기작업 스레드(스레드풀의 스레드)는 비동기작업이 끝나면 콜백메서드에서 

 

결과를 콘솔창에 뿌려주고 있습니다.

 

2. 주의점

 

두가지 주의할 점이 있습니다.

 

1. EndInvoke는 한번만 호출 할 수 있습니다.

 

콜백메서드에서 리턴값을 받아올지

 

비동기작업을 호출한 스레드에서 리턴값을 받아올지

 

한가지를 정해서 로직을 짜야한다는 점에 주의하셔야 됩니다.

 

양쪽에서 호출하면 컴파일에러가 납니다.

 

 

2. "다 더했다면 결과를 알려주세요"가 출력되는 시점을 잘 확인해야 합니다.

 

해당 문자열의 출력시점은 콜백메서드의 종료가 아닌, 비동기작업의 종료시점입니다.

 

즉 waitOne에서의 블락이 풀리는 시점은 콜백메서드의 종료가아닌 비동기작업의 끝입니다.

 

 

 

끝.

 

 

 

 

반응형

댓글