次の方法で共有


WF での非同期アクティビティの作成

AsyncCodeActivity は、派生アクティビティが非同期実行ロジックを実装できるようにする基底クラスをアクティビティ作成者に提供します。 これは、ワークフロー スケジューラ スレッドを保持せずに非同期作業を実行し、並列で実行できるアクティビティをブロックする必要があるカスタム アクティビティに役立ちます。 このトピックでは、 AsyncCodeActivityを使用してカスタム非同期アクティビティを作成する方法の概要について説明します。

AsyncCodeActivity の使用

System.Activities では、さまざまなアクティビティ作成要件に対して異なる基底クラスを持つカスタム アクティビティ作成者が提供されます。 それぞれが特定のセマンティックを保持し、ワークフロー作成者 (およびアクティビティ ランタイム) に対応するコントラクトを提供します。 AsyncCodeActivity ベースのアクティビティは、スケジューラ スレッドに対して非同期的に作業を実行し、その実行ロジックがマネージド コードで表されるアクティビティです。 非同期の結果として、 AsyncCodeActivity は実行中にアイドル ポイントを誘発する可能性があります。 非同期作業の揮発性のため、 AsyncCodeActivity では、アクティビティの実行期間中は常に非永続化ブロックが作成されます。 これにより、ワークフロー ランタイムが非同期作業の途中でワークフロー インスタンスを永続化できなくなり、非同期コードの実行中にワークフロー インスタンスがアンロードされるのを防ぐことができます。

AsyncCodeActivity メソッド

AsyncCodeActivityから派生するアクティビティは、BeginExecuteメソッドとEndExecuteメソッドをカスタム コードでオーバーライドすることで、非同期実行ロジックを作成できます。 ランタイムによって呼び出されると、これらのメソッドは AsyncCodeActivityContext渡されます。 AsyncCodeActivityContextを使用すると、アクティビティの作成者は、コンテキストのBeginExecute プロパティ内の/ EndExecuteUserState全体で共有状態を提供できます。 次の例では、 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から派生する必要があります。 次の例では、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: {r.Next(1, 101)}");
    }
}

戻り値がないため、DisplayRandomActionの代わりにFunc<T,TResult>を使用してデリゲートを呼び出し、デリゲートは値を返しません。

AsyncCodeActivity では、 Cancel オーバーライドも提供されます。 BeginExecuteEndExecuteは必須のオーバーライドですが、Cancelは省略可能であり、アクティビティが取り消されたり中止されたりしたときに未処理の非同期状態をクリーンアップできるようにオーバーライドできます。 クリーンアップが可能で、 AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequestedtrue場合、アクティビティは MarkCanceledを呼び出す必要があります。 このメソッドからスローされた例外は、ワークフロー インスタンスにとって致命的です。

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 プロパティで渡されたために発生する可能性があります。 これは、 BeginExecuteEndExecuteの間で状態を共有するための正しい方法です。 アクティビティ オブジェクトは複数のアクティビティ インスタンスによって参照される可能性があるため、派生クラスのメンバー変数 (この場合FileWriter ) を使用して BeginExecuteEndExecute の間で状態を共有するのは正しくありません。 メンバー変数を使用して状態を共有しようとすると、ある ActivityInstance の値が上書きされたり、別の ActivityInstanceの値が使用されたりする可能性があります。

引数値へのアクセス

AsyncCodeActivityの環境は、アクティビティで定義されている引数で構成されます。 これらの引数は、BeginExecute パラメーターを使用して、/EndExecuteAsyncCodeActivityContextオーバーライドからアクセスできます。 引数はデリゲート内でアクセスできませんが、引数の値またはその他の必要なデータは、そのパラメーターを使用してデリゲートに渡すことができます。 次の例では、 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を使用してアクティビティを作成する例については、「方法: アクティビティとアクティビティの作成オプションを作成する」を参照してください。

こちらも参照ください