PoolingPooling

このサンプルでは、Windows Communication Foundation (WCF) を拡張してオブジェクトプールをサポートする方法を示します。This sample demonstrates how to extend Windows Communication Foundation (WCF) to support object pooling. サンプルでは、エンタープライズ サービスのObjectPoolingAttribute 属性機能と、構文および意味が同じ属性を作成する方法を示します。The sample demonstrates how to create an attribute that is syntactically and semantically similar to the ObjectPoolingAttribute attribute functionality of Enterprise Services. オブジェクト プールにより、アプリケーションのパフォーマンスが大幅に向上します。Object pooling can provide a dramatic boost to an application's performance. ただし、適切に使用しないと逆効果になる場合があります。However, it can have the opposite effect if it is not used properly. オブジェクト プールは、負荷のかかる初期化が要求される、使用頻度の高いオブジェクトの再作成によるオーバーヘッドを減少させます。Object pooling helps reduce the overhead of recreating frequently used objects that require extensive initialization. ただし、プールされたオブジェクト上のメソッドへの呼び出しが完了するのに非常に時間がかかる場合、オブジェクト プールは、最大プール サイズに達するとすぐに追加要求をキューに置きます。However, if a call to a method on a pooled object takes a considerable amount of time to complete, object pooling queues additional requests as soon as the maximum pool size is reached. そのため、タイムアウト例外がスローされることによって、いくつかのオブジェクトの作成要求が失敗する場合があります。Thus it may fail to serve some object creation requests by throwing a timeout exception.

注意

このサンプルのセットアップ手順とビルド手順については、このトピックの最後を参照してください。The setup procedure and build instructions for this sample are located at the end of this topic.

WCF 拡張機能を作成するための最初の手順は、使用する機能拡張ポイントを決定することです。The first step in creating a WCF extension is to decide the extensibility point to use.

WCF では、ディスパッチャーとは、受信メッセージをユーザーのサービスのメソッド呼び出しに変換し、そのメソッドの戻り値を送信メッセージに変換する実行時コンポーネントを指します。In WCF the term dispatcher refers to a run-time component responsible for converting incoming messages into method invocations on the user’s service and for converting return values from that method to an outgoing message. WCF サービスによって、各エンドポイントのディスパッチャーが作成されます。A WCF service creates a dispatcher for each endpoint. クライアントに関連付けられたコントラクトが双方向コントラクトの場合、WCF クライアントは dispatcher を使用する必要があります。A WCF client must use a dispatcher if the contract associated with that client is a duplex contract.

チャネル ディスパッチャとエンドポイント ディスパッチャでは、ディスパッチャの動作を制御するさまざまなプロパティが公開されているため、チャネル全体の拡張とコントラクト全体の拡張を行うことができます。The channel and endpoint dispatchers offer channel-and contract-wide extensibility by exposing various properties that control the behavior of the dispatcher. さらに DispatchRuntime プロパティにより、ディスパッチ処理を検査、変更、またはカスタマイズすることもできます。The DispatchRuntime property also enables you to inspect, modify, or customize the dispatching process. このサンプルでは、サービス クラスのインスタンスを提供するオブジェクトをポイントする InstanceProvider プロパティに焦点を当てています。This sample focuses on the InstanceProvider property that points to the object that provides the instances of the service class.

IInstanceProviderThe IInstanceProvider

WCF では、ディスパッチャーはインターフェイスを実装するを使用して、サービスクラスのインスタンスを作成し InstanceProvider IInstanceProvider ます。In WCF, the dispatcher creates instances of the service class using a InstanceProvider, which implements the IInstanceProvider interface. このインターフェイスには、次の 3 つのメソッドが含まれています。This interface has three methods:

オブジェクト プールThe Object Pool

カスタム IInstanceProvider の実装により、サービスに必要なオブジェクト プールの意味が提供されます。A custom IInstanceProvider implementation provides the required object pooling semantics for a service. したがって、このサンプルにはプール用の ObjectPoolingInstanceProvider のカスタム実装を提供する IInstanceProvider 型が用意されています。Therefore, this sample has an ObjectPoolingInstanceProvider type that provides custom implementation of IInstanceProvider for pooling. Dispatcher が、新しいインスタンスを作成する代わりに GetInstance(InstanceContext, Message) メソッドを呼び出すと、カスタム実装はメモリ内プールで既存のオブジェクトを検索します。When the Dispatcher calls the GetInstance(InstanceContext, Message) method, instead of creating a new instance, the custom implementation looks for an existing object in an in-memory pool. 検索されたオブジェクトが使用可能な場合は、そのオブジェクトが返されます。If one is available, it is returned. それ以外の場合は、新しいオブジェクトが作成されます。Otherwise, a new object is created. GetInstance の実装を次のサンプル コードに示します。The implementation for GetInstance is shown in the following sample code.

object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)  
{  
    object obj = null;  
  
    lock (poolLock)  
    {  
        if (pool.Count > 0)  
        {  
            obj = pool.Pop();  
        }  
        else  
        {  
            obj = CreateNewPoolObject();  
        }  
        activeObjectsCount++;  
    }  
  
    WritePoolMessage(ResourceHelper.GetString("MsgNewObject"));  
  
    idleTimer.Stop();  
  
    return obj;
}  

カスタム ReleaseInstance 実装は、解放されたインスタンスをプールに戻し、ActiveObjectsCount 値をデクリメントします。The custom ReleaseInstance implementation adds the released instance back to the pool and decrements the ActiveObjectsCount value. Dispatcher はこれらのメソッドをさまざまなスレッドから呼び出すので、ObjectPoolingInstanceProvider クラスのクラス レベル メンバーへの同期アクセスが必要となります。The Dispatcher can call these methods from different threads, and therefore synchronized access to the class level members in the ObjectPoolingInstanceProvider class is required.

void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)  
{  
    lock (poolLock)  
    {  
        pool.Push(instance);  
        activeObjectsCount--;  
  
        WritePoolMessage(  
        ResourceHelper.GetString("MsgObjectPooled"));  
  
        // When the service goes completely idle (no requests
        // are being processed), the idle timer is started  
        if (activeObjectsCount == 0)  
            idleTimer.Start();
    }  
}  

メソッドは、 ReleaseInstance "初期化のクリーンアップ" 機能を提供します。The ReleaseInstance method provides a "clean up initialization" feature. 通常は、プールにはその有効期間中に最小限の数のオブジェクトが保持されます。Normally the pool maintains a minimum number of objects for the lifetime of the pool. ただし、使用率が非常に高くなり、プールでオブジェクトを追加作成する必要が生じて、その数が構成に指定されている上限に達する可能性があります。However, there can be periods of excessive usage that require creating additional objects in the pool to reach the maximum limit specified in the configuration. プールが最終的にアクティブでなくなると、そうした過剰なオブジェクトは余分なオーバーヘッドになります。Eventually, when the pool becomes less active, those surplus objects can become an extra overhead. したがって、activeObjectsCount がゼロに達すると、アイドル タイマが起動し、クリーンアップ サイクルがトリガされて実行されます。Therefore, when the activeObjectsCount reaches zero, an idle timer is started that triggers and performs a clean-up cycle.

動作の追加Adding the Behavior

ディスパッチャのレイヤ拡張は、次の動作を使用してフックされます。Dispatcher-layer extensions are hooked up using the following behaviors:

  • サービスの動作。Service Behaviors. サービス ランタイム全体のカスタマイズを実現します。These allow for the customization of the entire service runtime.

  • エンドポイントの動作。Endpoint Behaviors. サービス エンドポイントのカスタマイズ、特にチャネル ディスパッチャとエンドポイント ディスパッチャのカスタマイズを実現します。These allow for the customization of service endpoints, specifically a Channel and Endpoint Dispatcher.

  • コントラクトの動作。Contract Behaviors. クライアント上およびサービス上で、それぞれ ClientRuntime クラスと DispatchRuntime クラスのカスタマイズを実現します。These allow for the customization of both ClientRuntime and DispatchRuntime classes on the client and the service respectively.

オブジェクト プール拡張を行うには、サービスの動作を作成する必要があります。For the purpose of an object pooling extension a service behavior must be created. サービス動作を作成するには、IServiceBehavior インターフェイスを実装します。Service behaviors are created by implementing the IServiceBehavior interface. サービス モデルにカスタム動作を認識させるには、次のようないくつかの方法があります。There are several ways to make the service model aware of the custom behaviors:

  • カスタム属性を使用する。Using a custom attribute.

  • カスタム動作をサービス説明の動作コレクションに強制的に追加する。Imperatively adding it to the service description’s behaviors collection.

  • 構成ファイルを拡張する。Extending the configuration file.

このサンプルではカスタム属性を使用します。This sample uses a custom attribute. ServiceHost が構築されると、サービスの種類の定義で使用されている属性が調べられ、使用可能な動作がサービス説明の動作コレクションに追加されます。When the ServiceHost is constructed it examines the attributes used in the service’s type definition and adds the available behaviors to the service description’s behaviors collection.

インターフェイス IServiceBehavior には、ValidateAddBindingParameters、および ApplyDispatchBehavior の 3 つのメソッドがあります。The interface IServiceBehavior has three methods in it -- Validate, AddBindingParameters, and ApplyDispatchBehavior. Validate メソッドを使用すると、確実に動作をサービスに適用できます。The Validate method is used to ensure that the behavior can be applied to the service. このサンプルでは、これを実装することによって、サービスが Single を使用して構成されないようにします。In this sample, the implementation ensures that the service is not configured with Single. AddBindingParameters メソッドは、サービスのバインディングの構成に使用されます。The AddBindingParameters method is used to configure the service's bindings. このシナリオでは、このメソッドは必要ありません。It is not required in this scenario. ApplyDispatchBehavior はサービスのディスパッチャの構成に使用されます。The ApplyDispatchBehavior is used to configure the service's dispatchers. このメソッドは、が初期化されるときに WCF によって呼び出され ServiceHost ます。This method is called by WCF when the ServiceHost is being initialized. このメソッドには次のパラメータが渡されます。The following parameters are passed into this method:

  • Description: この引数は、サービス全体のサービスの説明を提供します。Description: This argument provides the service description for the entire service. これを使用すると、サービスのエンドポイント、コントラクト、バインディング、およびその他のデータに関する説明データを検査できます。This can be used to inspect description data about the service’s endpoints, contracts, bindings, and other data.

  • ServiceHostBase: この引数は、現在初期化中の ServiceHostBase を提供します。ServiceHostBase: This argument provides the ServiceHostBase that is currently being initialized.

カスタム IServiceBehavior 実装では、ObjectPoolingInstanceProvider の新しいインスタンスがインスタンス化され、ServiceHostBase の各 InstanceProvider 内の DispatchRuntime プロパティに割り当てられます。In the custom IServiceBehavior implementation a new instance of ObjectPoolingInstanceProvider is instantiated and assigned to the InstanceProvider property in each DispatchRuntime in the ServiceHostBase.

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)  
{  
    // Create an instance of the ObjectPoolInstanceProvider.  
    ObjectPoolingInstanceProvider instanceProvider = new  
           ObjectPoolingInstanceProvider(description.ServiceType,
                                                    minPoolSize);  
  
    // Forward the call if we created a ServiceThrottlingBehavior.  
    if (this.throttlingBehavior != null)  
    {  
        ((IServiceBehavior)this.throttlingBehavior).ApplyDispatchBehavior(description, serviceHostBase);  
    }  
  
    // In case there was already a ServiceThrottlingBehavior
    // (this.throttlingBehavior==null), it should have initialized
    // a single ServiceThrottle on all ChannelDispatchers.
    // As we loop through the ChannelDispatchers, we verify that
    // and modify the ServiceThrottle to guard MaxPoolSize.  
    ServiceThrottle throttle = null;  
  
    foreach (ChannelDispatcherBase cdb in
            serviceHostBase.ChannelDispatchers)  
    {  
        ChannelDispatcher cd = cdb as ChannelDispatcher;  
        if (cd != null)  
        {  
            // Make sure there is exactly one throttle used by all
            // endpoints. If there were others, we could not enforce
            // MaxPoolSize.  
            if ((this.throttlingBehavior == null) &&
                        (this.maxPoolSize != Int32.MaxValue))  
            {  
                throttle ??= cd.ServiceThrottle;
                if (cd.ServiceThrottle == null)  
                {  
                    throw new
InvalidOperationException(ResourceHelper.GetString("ExNullThrottle"));  
                }  
                if (throttle != cd.ServiceThrottle)  
                {  
                    throw new InvalidOperationException(ResourceHelper.GetString("ExDifferentThrottle"));  
                }  
             }  
  
             foreach (EndpointDispatcher ed in cd.Endpoints)  
             {  
                 // Assign it to DispatchBehavior in each endpoint.  
                 ed.DispatchRuntime.InstanceProvider =
                                      instanceProvider;  
             }  
         }  
     }  
  
     // Set the MaxConcurrentInstances to limit the number of items
     // that will ever be requested from the pool.  
     if ((throttle != null) && (throttle.MaxConcurrentInstances >
                                      this.maxPoolSize))  
     {  
         throttle.MaxConcurrentInstances = this.maxPoolSize;  
     }  
}  

IServiceBehavior 実装のほかにも、ObjectPoolingAttribute クラスには属性引数を使用してオブジェクト プールをカスタマイズするいくつかのメンバがあります。In addition to an IServiceBehavior implementation the ObjectPoolingAttribute class has several members to customize the object pool using the attribute arguments. こうしたメンバには MaxPoolSizeMinPoolSizeCreationTimeout などがあり、.NET Enterprise Services で提供されるオブジェクト プール機能のセットに一致します。These members include MaxPoolSize, MinPoolSize, and CreationTimeout, to match the object pooling feature set provided by .NET Enterprise Services.

新しく作成されたカスタム属性を使用してサービス実装に注釈を付けることにより、オブジェクトプール動作を WCF サービスに追加できるようになりました ObjectPoolingThe object pooling behavior can now be added to a WCF service by annotating the service implementation with the newly created custom ObjectPooling attribute.

[ObjectPooling(MaxPoolSize=1024, MinPoolSize=10, CreationTimeout=30000)]
public class PoolService : IPoolService  
{  
  // …  
}  

サンプルの実行Running the Sample

このサンプルでは、特定のシナリオでオブジェクト プールを使用することによって得られるパフォーマンス上の利点を示します。The sample demonstrates the performance benefits that can be gained by using object pooling in certain scenarios.

サービス アプリケーションは、WorkServiceObjectPooledWorkService の 2 つのサービスを実装します。The service application implements two services -- WorkService and ObjectPooledWorkService. どちらのサービスも同じ実装を共有し、負荷のかかる初期化を必要とします。さらに、比較的負荷の少ない DoWork() メソッドを公開します。Both services share the same implementation -- they both require expensive initialization and then expose a DoWork() method that is relatively cheap. 両者の唯一の違いは、ObjectPooledWorkService ではオブジェクト プールが次のように構成されるという点です。The only difference is that the ObjectPooledWorkService has object pooling configured:

[ObjectPooling(MinPoolSize = 0, MaxPoolSize = 5)]  
public class ObjectPooledWorkService : IDoWork  
{  
    public ObjectPooledWorkService()  
    {  
        Thread.Sleep(5000);  
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService instance created.");  
    }  
  
    public void DoWork()  
    {  
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService.GetData() completed.");  
    }
}  

クライアントを実行すると、WorkService への呼び出し時間が 5 回計算されます。When you run the client, it times calling the WorkService 5 times. 次に、ObjectPooledWorkService への呼び出し時間が 5 回計算されます。It then times calling the ObjectPooledWorkService 5 times. その後、時間の違いが次のように表示されます。The difference in time is then displayed:

Press <ENTER> to start the client.  
  
Calling WorkService:  
1 - DoWork() Done  
2 - DoWork() Done  
3 - DoWork() Done  
4 - DoWork() Done  
5 - DoWork() Done  
Calling WorkService took: 26722 ms.  
Calling ObjectPooledWorkService:  
1 - DoWork() Done  
2 - DoWork() Done  
3 - DoWork() Done  
4 - DoWork() Done  
5 - DoWork() Done  
Calling ObjectPooledWorkService took: 5323 ms.  
Press <ENTER> to exit.  

注意

クライアントを最初に実行したときには、両方のサービスにかかった時間は同じように見えます。The first time the client is run both services appear to take about the same amount of time. サンプルを再実行すると、ObjectPooledWorkService の戻り時間の方が早いことがわかります。オブジェクトのインスタンスが既にプール内に存在しているからです。If you re-run the sample, you can see that the ObjectPooledWorkService returns much quicker because an instance of that object already exists in the pool.

サンプルをセットアップ、ビルド、および実行するにはTo set up, build, and run the sample

  1. Windows Communication Foundation サンプルの1回限りのセットアップ手順を実行したことを確認します。Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. サンプルを単一コンピューター構成または複数コンピューター構成で実行するには、「 Windows Communication Foundation サンプルの実行」の手順に従います。To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

注意

Svcutil.exe を使用してこのサンプルの構成を再生成した場合は、クライアント コードに一致するように、クライアント構成内のエンドポイント名を変更してください。If you use Svcutil.exe to regenerate the configuration for this sample, be sure to modify the endpoint name in the client configuration to match the client code.

重要

サンプルは、既にコンピューターにインストールされている場合があります。The samples may already be installed on your machine. 続行する前に、次の (既定の) ディレクトリを確認してください。Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

このディレクトリが存在しない場合は、 Windows Communication Foundation (wcf) および Windows Workflow Foundation (WF) のサンプルの .NET Framework 4にアクセスして、すべての WINDOWS COMMUNICATION FOUNDATION (wcf) とサンプルをダウンロードして WFWF ください。If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. このサンプルは、次のディレクトリに格納されます。This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Instancing\Pooling