非同期処理を行うAsyncCodeActivity
WF 4ではParallel~と名前の付いたアクティビティが用意されているので、非同期処理を行えるように思えますが実際には皆さんがイメージされるであろうマルチタスキング的な非同期処理は行うことができません。Parallelアクティビティの実際の挙動は、次のようになります。
何かしらの重たい処理が実行され、アクティビティ内部にて待機状態が発生します。WFランタイムは待機状態を検知すると、待機している他のアクティビティへと制御を遷移させます。同様に遷移先で待機状態が発生すると、さらに他の待機アクティビティへと遷移していきます。実際には待機状態に遷移した際に、WFランタイムが次にキューイングされている処理を判断し実行を遷移させています。このように非同期処理と言っても、特定のアクティビティでワークフロー全体が待機状態に陥らないようにするためのものとなっています。
本当の意味で並列処理を行うためには、アクティビティ内部でスレッドを作成し処理を実行するのと、制御を受け取ったら即座に制御をWFランタイムに戻してあげるようにすることで対応が可能です。細かい実装については私のBlog記事にてまとめてあるので、参考にしてみてください。
しかし、重い処理をアクティビティに行わせ、ワークフローの実行スレッドを長期にわたり押さえてしまうと、問題のあるケースがやはり発生します。そのような場合に利用するのが非同期処理を行うためのAsyncCodeActivityです。これはアクティビティの処理を非同期にて行うことが可能になります。また非同期で行う際にはアイドル状態になることもあるかと思いますが、その際にWFランタイムによる永続化やアンロードが行われないようにすることも可能です。
AsyncCodeActivityの利用サンプルを次に示します。
Imports System.Activities
Public Class SampleAsyncActivity
Inherits AsyncCodeActivity
Private Delegate Sub AsyncOutputConsole()
Protected Overrides Function BeginExecute(context As System.Activities.AsyncCodeActivityContext, callback As System.AsyncCallback, state As Object) As System.IAsyncResult
Dim outDel = New AsyncOutputConsole(AddressOf OutputConsole)
context.UserState = outDel
Return outDel.BeginInvoke(callback, state)
End Function
Protected Overrides Sub EndExecute(context As System.Activities.AsyncCodeActivityContext, result As System.IAsyncResult)
Dim outDel As AsyncOutputConsole = context.UserState
'非同期処理終了後の何らかの処理
End Sub
Private Sub OutputConsole()
Console.WriteLine("非同期アクティビティからの出力です")
End Sub
End Class
Imports System.Activities
Module Module1
Sub Main()
Dim act As New SampleAsyncActivity
Dim wfApp As New WorkflowApplication(act)
wfApp.Run()
'タイミングが重なるようにウェイトをかける(環境によりウェイトは変化させてください)
System.Threading.Thread.Sleep(31)
For cnt = 0 To 100
Console.WriteLine("ホスト側からの出力です")
Next
Console.ReadLine()
End Sub
End Module
using System;
using System.Activities;
namespace SampleAsyncActivity
{
class SampleAsyncActivity: AsyncCodeActivity
{
private delegate void AsyncOutputConsole();
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
var outDel = new AsyncOutputConsole(OutputConsole);
context.UserState = outDel;
return outDel.BeginInvoke(callback, state);
}
protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
var outDel = (AsyncOutputConsole)context.UserState;
//非同期処理終了後の何らかの処理
}
private void OutputConsole()
{
Console.WriteLine("非同期アクティビティからの出力です");
}
}
}
using System;
using System.Activities;
using System.Activities.Statements;
namespace SampleAsyncActivity
{
class Program
{
static void Main(string[] args)
{
var act = new SampleAsyncActivity();
var wfApp = new WorkflowApplication(act);
wfApp.Run();
//タイミングが重なるようにウェイトをかける(環境によりウェイトは変化させてください)
System.Threading.Thread.Sleep(31);
for (int cnt = 0; cnt < 100; cnt++)
{
Console.WriteLine("ホスト側からの出力です");
}
Console.ReadLine();
}
}
}
実行するとホスト側の出力とワークフロー側の出力が混じったこのような形で出力されます。サンプルとして用意したのは、単純に標準出力に文字を出力するアクティビティです。今回の例はあまり非同期処理に適したものではありませんが、ファイル処理やデータベース処理など非同期で行うのが望ましい処理は多くの場面で起こりえます。そのような場合は、AsyncCodeActivityを利用した非同期アクティビティを作成することで、WFランタイムの動作に影響を与えることなくスムーズに動作させることが可能になります。

