async-await を使用した非同期サービスは、多くのクライアント呼び出しをインターリーブして並行して実行できるため、非常に応答性が高くなります (2)。それにもかかわらず、サービスは 1 つのスレッド (3) で完全にスレッドセーフに実行でき、セッションまたは呼び出し専用のフレームワークによって作成されたシングルトン サービス (1) またはサービス オブジェクトにすることができます。
サービスを実装するときは、 ServiceBehaviourAttributes (1)...(3) に注意してください。
[ServiceContract( Namespace="X", Name="TheContract" )]
public interface IAsyncContractForClientAndService
{
[OperationContract]
Task<TResponse> SendReceiveAsync( TRequest req );
}
[ServiceBehavior (InstanceContextMode = InstanceContextMode.Single, // (1)
// also works with InstanceContextMode.PerSession or PerCall
ConcurrencyMode = ConcurrencyMode.Multiple, // (2)
UseSynchronizationContext = true)] // (3)
public MyService : IAsyncContractForClientAndService
{
public async Task<TResponse> SendReceiveAsync( TRequest req )
{
DoSomethingSynchronous();
await SomethingAsynchronous();
// await lets other clients call the service here or at any await in
// subfunctions. Calls from clients execute 'interleaved'.
return new TResponse( ... );
}
}
すべての呼び出しを 1 つのスレッドで実行するには、ServiceHost を Open() するときに System.Threading.SynchronizationContext.Current != null が存在する必要があります。SynchronizationContext を使用すると、ロックを気にする必要がなくなります。アトミックで割り込み不可能なコード セクションは、ある await から次の await に大まかに伸びます。共有サービスのデータが待機するたびに一貫した状態になるように注意し、1 つのクライアントからの連続する要求は、送信された順序ではなく応答される可能性があることに注意してください。
クライアント側では、非同期サービス操作が待機可能です。
var response = await client.Channel.SendReceiveAsync( request );
操作コントラクトでoutまたはrefパラメーターを使用することはできません。すべての応答データは、戻り値 Task(T) によって渡される必要があります。
私はこのインターフェイスをAsyncWcfLibで使用します。これはアクター ベースのプログラミング モデルをサポートします。