Programming/.NET

속도가 느린 입출력장치와의 통신이나 혹은 다른 메서드를 호출했을때 처리가 완료되기를 기다리지 않고 현재 스레드에서 곧장 제어를 넘겨받아 작업을 계속진행하는 방식을 비동기 호출(asynchronous call)이라고 합니다.

 

class Program
{
    delegate void callMethod();

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(null, null);

        Console.WriteLine("현재 스레드 완료");

        cm.EndInvoke(ar);

        Console.Read();
    }

    static void myMethod()
    {
        Console.WriteLine("메서드 실행");

        Thread.Sleep(10000);

        Console.WriteLine("완료");
    }
}

 

위 예제는 메서드의 실행을 호출하되 비동기로 호출하는 방식을 보여주고 있습니다. 우선 메서드의 형식에 대한 델리게이트(delegate)를 선언하고 해당 델리게이트의 BeginInvoke로 메서드를 호출합니다.

 

EndInvoke는 메서드에서 반환값을 받는 경우에 필요하지만 그렇지 않다면 생략이 가능합니다. 다만 반환값이 없더라도 호출해줄것을 권장합니다.

 

BeginInvoke가 호출되면 CPU는 스레드풀에서 대기중인 스레드를 불러와 메서드의 실행을 맡김으로서 비동기를 실현합니다. 그리고 메서드가 종료되면 해당 스레드는 스레드의 상태를 나타내느 상태값을 Signal로 바꾸게 되는데 BeginInvoke에서 반환하는 IAsyncResult타입의 AsyncWaitHandle속성이 스레드의 상태를 대기하는 EventWaitHandle과 같으므로 필요시 이 속성을 사용해 스레드의 실행을 동기화할 수 있습니다.

 

static void Main(string[] args)
{
    callMethod cm = new callMethod(myMethod);
    IAsyncResult ar = cm.BeginInvoke(null, null);
    ar.AsyncWaitHandle.WaitOne();

    Console.WriteLine("현재 스레드 완료");

    cm.EndInvoke(ar);

    Console.Read();
}

 

위와 같이 메서드를 비동기로 호출할때 만약 메서드에 특정 값을 매개변수로 전달해야 한다면 다음과 같이 처리할 수 있습니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(10, 20, null, null);

        Console.WriteLine("현재 스레드 완료");

        int sum = cm.EndInvoke(ar);

        Console.WriteLine("결과 : " + sum);
        Console.Read();
    }

    static int myMethod(int i, int j)
    {
        Thread.Sleep(10000);

        return i + j;
    }
}

 

myMethod에서는 2개의 정수값을 받아 이를 합한 결과를 반환하도록 하였습니다. 그에 따라 델리게이트(delegate)도 같은 형식으로 수정하고 BeginInvoke호출시 필요한 매개변수를 추가해줍니다. 결과값은 이전에 말씀드린대로 EndInvoke를 통해 받을 수 있습니다.

 

비동기 메서드 호출이후 별도의 메서드를 호출하는 콜백메서드가 필요하다면 원하는 메서드를 정의하고 BeginInvoke의 세번째 매개변수에 해당 메서드를 지정해주기만 하면 됩니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(10, 20, methodComplete, null);

        Console.WriteLine("현재 스레드 완료");

        int sum = cm.EndInvoke(ar);

        Console.WriteLine("결과 : " + sum);
        Console.Read();
    }

    static int myMethod(int i, int j)
    {
        Thread.Sleep(10000);

        return i + j;
    }

    static void methodComplete(IAsyncResult ar)
    {
        Console.WriteLine("완료");
    }
}

 

지금까지는 임의의 사용자 메서드를 어떻게 비동기로 호출하는지를 알아봤는데 .NET의 몇몇클래스에도 비동기로 호출할 수 있는 장치가 마련되어 있습니다. 예를 들어 파일을 스트림(Stream)으로 읽어들이는 FileStream클래스의 경우

 

static void Main(string[] args)
{
    using (FileStream fs = new FileStream("C:\\sampe.txt", FileMode.Open, FileAccess.Read)) {
        byte[] b = new byte[fs.Length];
        fs.Read(b, 0, b.Length);

        string s = Encoding.UTF8.GetString(b);
    }

    Console.Read();
}

 

일반적으로 위와 같이 사용할 수 있지만 파일이 너무 크거나 하는등의 이유로 파일의 내용을 다 읽을때까지 대기할 수 없다면 아래와 같이 비동기로의 호출이 가능합니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        FileStream fs = new FileStream("C:\\sample.txt", FileMode.Open, FileAccess.Read);
        byte[] b = new byte[fs.Length];
        fs.BeginRead(b, 0, b.Length, readComplete, b);

        Console.WriteLine("주 스레드 완료");
        Console.Read();
    }
    static void readComplete(IAsyncResult ar)
    {
        byte[] b = (byte[])ar.AsyncState;
        string s = Encoding.UTF8.GetString(b);
        Console.WriteLine(s);
    }
}

 

대부분의 클래스에서 비동기를 지원하는 경우 메서드는 Begin-- 으로 시작합니다. FileStream도 마찬가지 인데 Read메서드의 비동기 호출이 BeginRead이며 BeginRead 메서드를 호출할때 동작 완료시 사용될 메서드를 지정합니다.

'Programming > .NET' 카테고리의 다른 글

[C#] ? / Nullable  (0) 2019.02.12
[C#] partial  (0) 2019.02.07
[C#] 비동기 호출(asynchronous call)  (0) 2019.01.23
Debug / Release  (0) 2019.01.15
[ASP.NET MVC] Razor  (0) 2019.01.08
[ASP.NET MVC] URL 다루기  (0) 2018.12.18
0 0