2016-02-10 45 views
5

私はスピンアップするのに "高価な"サードパーティのコンポーネントを持っています。このコンポーネントはスレッドセーフではありません。上記のコンポーネントはWCFサービスの中でホストされているので(今のところ)、サービスを呼び出すたびに新しいコンポーネントを作成する必要があります。私が代わりにやってみたい何"固定"/"ロードバランス" C#スレッドプール?

は、各コンポーネントの独自のコピーをスピンアップし、メソッドを呼び出すための仕組みを持っており、それが16件のスレッドの1に配布していると持っている16個のスレッドを言うのプールを持っています戻り値

だから、のような単純なもの:私は続行する応答を必要として、それが応答を取得するまで呼び出しがブロックするため

var response = threadPool.CallMethod(param1, param2); 

その罰金。

提案がありますか?たぶん私はそれと16件のスレッドによってサービスされるConcurrentQueueが仕事をするだろうoverthinkingんだけど、メソッドの戻り値は、呼び出し元に返さなるだろうか、今確認してください?あなたはそれだけでひどく行くために起こっていることの上にスレッド管理の層を追加するので、もし

+0

は、コンポーネントについての詳細を知る必要があります。プールが排水された後

EDIT

BasicはGetどのブロックとプールの実装「Xインスタンスのセットを固定しました」。 1)それはCOMコンポーネントですか? 2)どのようにWCFサービスをホストしていますか? IIS? Windowsサービスの中に?コンソールアプリですか? 3)WCFサービスのインスタンスコントロールとは何ですか?シングルトン?コールごとに?セッションごとに? – MickyD

+2

@MickyDによると、コンポーネントの新しいインスタンスを作成するための呼び出しをスレッドしても、スレッドの安全性の問題は解決されません(実際には悪化すると思います)。 – AWinkle

+3

本当に必要なのは、スレッドプールではなくオブジェクトプールです。 –

答えて

2

WCFは、すでにそのリソースを管理するためのスレッドプールを使用します。可能な場合は、サービスコールの競合を避けるようにしないでください。

私は一度だけあなたの高価なオブジェクトで初期化になるだろう単一ThreadLocalまたはスレッドのスタティックを使用している状況でどうなるのか。その後、スレッドプールスレッドが利用できるようになります。あなたのオブジェクトがMTAスレッドに微細であると仮定している

。私はそれがあなたの記事からのものだと思っている。

あまりにも多くのオブジェクトが作成されますと、プールが大きくなりすぎて、あなたがあまりにも多くのメモリを使用することが懸念されます。しかし、これが他のことを行う前に実際にそうであるかどうかを確認してください。これは簡単に実装できる非常に単純な戦略です。あなたが本当に必要な場合にのみ、より複雑になります。

+0

ええ、私が探しているのはオブジェクトプールで、スレッドプールではないと思います。オブジェクトはスレッドセーフではありません。複数のスレッドが同じインスタンスにアクセスすると、クラッシュします。 WCFサービスがスレッドを作成して破棄するため、ThreadLocalは役に立ちません。 – SledgeHammer

+0

実際にそれを実際にテストしましたか?それはまあまあかもしれませんが、この人はスレッドプールからスレッドを引っ張ると考えています:http://stackoverflow.com/questions/5288991/destroy-a-wcf-thread。彼は状態を格納するためにスレッド統計を使用しないように言いますが、ここでは状態を格納していません。 – briantyler

2

何よりもまず、私は@briantylerに同意する:ThreadLocal<T>または静的フィールドのスレッドが何をしたいと考えられます。それを出発点にして、それがあなたのニーズを満たしていない場合は、他の選択肢も検討してください。

複雑で柔軟代替は、シングルトンオブジェクトプールです。その最も単純な形式では、あなたのプールの種類は次のようになります。あなたがプールとして、プリミティブクラスまたは構造体(すなわちObjectPool<MyComponent>)の観点タイプTを考えている場合

public sealed class ObjectPool<T> 
{ 
    private readonly ConcurrentQueue<T> __objects = new ConcurrentQueue<T>(); 
    private readonly Func<T> __factory; 

    public ObjectPool(Func<T> factory) 
    { 
     __factory = factory; 
    } 

    public T Get() 
    { 
     T obj; 
     return __objects.TryDequeue(out obj) ? obj : __factory(); 
    } 

    public void Return(T obj) 
    { 
     __objects.Enqueue(obj); 
    } 
} 

これはとても便利いないようです建てられた任意のスレッドのコントロールを持っていません。しかし、あなたはLazy<T>Task<T>モナドのためのあなたのタイプTを代用し、正確に何をしたい得ることができます。

プールの初期化:

Func<Task<MyComponent>> factory =() => Task.Run(() => new MyComponent()); 
ObjectPool<Task<MyComponent>> pool = new ObjectPool<Task<MyComponent>>(factory); 

// "Pre-warm up" the pool with 16 concurrent tasks. 
// This starts the tasks on the thread pool and 
// returns immediately without blocking. 
for (int i = 0; i < 16; i++) { 
    pool.Return(pool.Get()); 
} 

使用法:

// Get a pooled task or create a new one. The task may 
// have already completed, in which case Result will 
// be available immediately. If the task is still 
// in flight, accessing its Result will block. 
Task<MyComponent> task = pool.Get(); 

try 
{ 
    MyComponent component = task.Result; // Alternatively you can "await task" 

    // Do something with component. 
} 
finally 
{ 
    pool.Return(task); 
} 

この方法はThreadLocalまたはスレッドの静的フィールドであなたのコンポーネントを維持するよりも複雑ですが、制限のような派手な何かをする必要がある場合プールされたインスタンスの数、プールの抽象化は非常に便利です。

public sealed class ObjectPool<T> 
{ 
    private readonly Queue<T> __objects; 

    public ObjectPool(IEnumerable<T> items) 
    { 
     __objects = new Queue<T>(items); 
    } 

    public T Get() 
    { 
     lock (__objects) 
     { 
      while (__objects.Count == 0) { 
       Monitor.Wait(__objects); 
      } 

      return __objects.Dequeue(); 
     } 
    } 

    public void Return(T obj) 
    { 
     lock (__objects) 
     { 
      __objects.Enqueue(obj); 

      Monitor.Pulse(__objects); 
     } 
    } 
} 
+0

ええ、私はこれが行く方法だと思います。オブジェクトプールを調整して、Xインスタンスの固定セットを持たずに、無制限にオンザフライで作成するだけです。 – SledgeHammer

+0

@SledgeHammerの場合、オブジェクトプールとして 'BlockingCollection 'を使用することができます。すべてのプールされたオブジェクトがリースされた状況に遭遇した場合、次のコンシューマはインスタンスの1つが(Addを介して)返されるまで、Take()をブロックします。 –

+0

また、プールされたインスタンス数が希望の値を超えて増加した場合、 'Return'が返されたインスタンスを破棄するプールも考えています。これにより、絶対に必要な場合に新しいインスタンスを割り当てることができますが、余剰はすべて回収され、無限の成長を防ぎます。 –

関連する問題