다음을 통해 공유


WF에서 비동기 활동 만들기

AsyncCodeActivity는 활동 작성자가 파생 활동에서 비동기 실행 논리를 구현하는 데 사용할 수 있는 기본 클래스를 제공합니다. 이 기능은 워크플로 스케줄러 스레드를 유지하지 않고 병렬로 실행 가능한 활동을 차단하지 않으면서 비동기 작업을 수행해야 하는 사용자 지정 활동에 유용합니다. 이 항목에서는 AsyncCodeActivity를 사용하여 사용자 지정 비동기 활동을 만드는 방법을 간략하게 설명합니다.

AsyncCodeActivity 사용

System.Activities는 사용자 지정 활동 작성자에게 활동 작성 요구 사항별로 다른 기본 클래스를 제공합니다. 각 클래스는 특정 체계를 따르고 워크플로 작성자 및 활동 런타임에 해당 계약을 제공합니다. AsyncCodeActivity 기반 활동은 스케줄러 스레드를 기준으로 비동기 작업을 수행하고 실행 논리가 관리 코드에 표시되는 활동입니다. 비동기 처리로 인해 AsyncCodeActivity에서 실행 중에 유휴 지점이 발생할 수 있습니다. 비동기 작업의 일시적 특성으로 인해 AsyncCodeActivity는 활동 실행 기간 동안 항상 비지속성 블록을 만듭니다. 따라서 비동기 작업 중에 워크플로 런타임에 워크플로 인스턴스가 유지되지 않을 뿐만 아니라 비동기 코드를 실행하는 동안 워크플로 인스턴스가 언로드되지 않습니다.

AsyncCodeActivity 메서드

AsyncCodeActivity에서 파생되는 활동은 사용자 지정 코드로 BeginExecuteEndExecute 메서드를 재정의하여 비동기 실행 논리를 만들 수 있습니다. 런타임에 의해 호출되면 이러한 메서드가 AsyncCodeActivityContext에 전달됩니다. AsyncCodeActivityContext를 사용하여 활동 작성자가 컨텍스트의 UserState 속성에서 BeginExecute/ EndExecute를 통해 공유 상태를 제공할 수 있습니다. 다음 예제에서 GenerateRandom 활동은 난수를 비동기적으로 생성합니다.

public sealed class GenerateRandom : AsyncCodeActivity<int>
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int> GetRandomDelegate = new Func<int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int> GetRandomDelegate = (Func<int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, 101);
    }
}

이전 예제 활동은 AsyncCodeActivity<TResult>에서 파생되며 OutArgument<int>라는 높은 Result를 갖습니다. GetRandom 메서드에 의해 반환되는 값은 EndExecute 재정의를 통해 추출되어 반환되며, 이 값이 Result 값으로 설정됩니다. 결과를 반환하지 않는 비동기 활동은 AsyncCodeActivity에서 파생되어야 합니다. 다음 예제에서는 DisplayRandom에서 파생되는 AsyncCodeActivity 활동을 정의합니다. 이 활동은 GetRandom 활동과 비슷하지만 결과를 반환하는 대신 콘솔에 메시지를 표시합니다.

public sealed class DisplayRandom : AsyncCodeActivity
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Action GetRandomDelegate = new Action(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Action GetRandomDelegate = (Action)context.UserState;
        GetRandomDelegate.EndInvoke(result);
    }

    void GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        Console.WriteLine("Random Number: {0}", r.Next(1, 101));
    }
}

반환 값이 없으므로 DisplayRandomAction 대신 Func<T,TResult>을 사용하여 대리자를 호출하고 대리자는 아무 값도 반환하지 않습니다.

또한 AsyncCodeActivityCancel 재정의를 제공합니다. BeginExecuteEndExecute는 재정의가 필요하지만, Cancel은 선택 사항이며, 활동에서 처리 중인 비동기 상태를 취소하거나 중단할 때 비동기 상태를 정리할 수 있도록 재정의할 수 있습니다. 정리할 수 있고 AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequestedtrue이면 활동에서 MarkCanceled를 호출해야 합니다. 이 메서드에서 throw되는 예외는 워크플로 인스턴스에 치명적입니다.

protected override void Cancel(AsyncCodeActivityContext context)
{
    // Implement any cleanup as a result of the asynchronous work
    // being canceled, and then call MarkCanceled.
    if (context.IsCancellationRequested)
    {
        context.MarkCanceled();
    }
}

클래스에 대한 비동기 메서드 호출

.NET Framework의 많은 클래스는 비동기 기능을 제공합니다. 이 기능은 AsyncCodeActivity 기반 활동을 사용하여 비동기적으로 호출할 수 있습니다. 다음 예제에서는 FileStream 클래스를 사용하여 파일을 비동기적으로 생성하는 활동을 만듭니다.

public sealed class FileWriter : AsyncCodeActivity
{
    public FileWriter()
        : base()
    {
    }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        string tempFileName = Path.GetTempFileName();
        Console.WriteLine("Writing to file: " + tempFileName);

        FileStream file = File.Open(tempFileName, FileMode.Create);

        context.UserState = file;

        byte[] bytes = UnicodeEncoding.Unicode.GetBytes("123456789");
        return file.BeginWrite(bytes, 0, bytes.Length, callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        FileStream file = (FileStream)context.UserState;

        try
        {
            file.EndWrite(result);
            file.Flush();
        }
        finally
        {
            file.Close();
        }
    }
}

BeginExecute 및 EndExecute 메서드 간에 상태 공유

앞의 예제에서 FileStream에서 만든 BeginExecute 개체는 EndExecute에서 액세스합니다. 이는 file 변수가 AsyncCodeActivityContext.UserState에서 BeginExecute 속성에 전달되기 때문입니다. 이는 BeginExecuteEndExecute 간의 상태 공유에 올바른 메서드입니다. 파생 클래스(FileWriter in this case)에서 멤버 변수를 사용하여 BeginExecuteEndExecute간에 상태를 공유하는 것은 활동 개체가 여러 활동 인스턴스에서 참조될 수 있으므로 잘못된 것입니다. 멤버 변수를 사용하여 상태를 공유하려고 시도한 경우 어떤 ActivityInstance의 값이 다른 ActivityInstance의 값을 덮어쓰거나 사용하게 될 수 있습니다.

인수 값 액세스

AsyncCodeActivity 환경은 활동에 정의된 인수들로 구성됩니다. 이러한 인수는 BeginExecute/EndExecute 재정의에서 AsyncCodeActivityContext 매개 변수를 사용하여 액세스할 수 있습니다. 대리자에서는 인수에 액세스할 수 없지만 해당 매개 변수를 사용하여 인수 값 또는 원하는 다른 데이터를 대리자에 전달할 수 있습니다. 다음 예제에서는 Max 인수에서 상한(포함)을 가져오는 난수 생성 활동을 정의합니다. 대리자를 호출하면 인수 값이 비동기 코드에 전달됩니다.

public sealed class GenerateRandomMax : AsyncCodeActivity<int>
{
    public InArgument<int> Max { get; set; }

    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int, int> GetRandomDelegate = new Func<int, int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(Max.Get(context), callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int, int> GetRandomDelegate = (Func<int, int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom(int max)
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, max + 1);
    }
}

AsyncCodeActivity를 사용하여 작업 또는 자식 활동 예약

AsyncCodeActivity 파생 사용자 지정 활동은 워크플로 스레드와 관련하여 작업을 비동기식으로 수행하는 메서드를 제공하지만 자식 활동이나 작업을 예약하는 기능은 제공하지 않습니다. 단 비동기식 동작은 구성을 통해 자식 활동의 예약으로 통합할 수 있습니다. 비동기 작업을 만든 다음 Activity 또는 NativeActivity 파생 작업을 통해 구성하여 비동기식 동작을 제공하고 자식 활동 또는 작업을 예약할 수 있습니다. 예를 들어 Activity에서 파생되고, 활동의 논리를 구현하는 다른 활동뿐만 아니라 비동기 활동을 포함하는 Sequence 구현으로 포함되도록 활동을 만들 수 있습니다. ActivityNativeActivity를 사용하여 활동을 구성하는 방법에 대한 추가 예제는 방법: 활동 만들기활동 작성 옵션을 참조하세요.

참고 항목