다음을 통해 공유


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>에서 파생되었으며, Result라는 이름의 향상된 OutArgument<int>을 가지고 있습니다. 메서드 GetRandom에서 반환된 값은 EndExecute 재정의를 통해 추출되어 반환되며, 이 값을 Result 값으로 설정합니다. 결과를 반환하지 않는 비동기 활동은 .에서 AsyncCodeActivity파생되어야 합니다. 다음 예제에서는 AsyncCodeActivity에서 파생된 DisplayRandom 활동이 정의됩니다. 이 활동은 활동과 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: {r.Next(1, 101)}");
    }
}

반환 값이 없으므로 DisplayRandom는 대리자를 호출할 때 Func<T,TResult> 대신 Action을 사용하며, 대리자는 값을 반환하지 않습니다.

또한 AsyncCodeActivityCancel 재정의를 제공합니다. BeginExecuteEndExecute은(는) 필수 재정의 항목이며, Cancel은(는) 선택 사항이지만, 작업이 취소되거나 중단될 때 미해결 비동기 상태를 정리할 수 있도록 재정의할 수 있습니다. 정리가 가능하고 AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested 있는 true경우 활동에서 호출 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.UserStateBeginExecute 속성에 전달되었기 때문에 가능합니다. 이 메서드는 상태를 BeginExecute 공유하는 데 적합한 방법입니다.EndExecute 여러 활동 인스턴스에서 활동 개체를 참조할 수 있기 때문에 파생 클래스(FileWriter 이 경우)에서 멤버 변수를 사용하여 상태를 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를 사용하여 활동을 구성하는 방법에 대한 더 많은 예시는 방법: 활동 만들기활동 작성 옵션을 참조하세요.

참고하십시오