要件は、コンシューマープロデューサーキューを構築することのようです。どのプロデューサーがリストにメッセージを追加し続け、コンシューマーがそのリストからアイテムを選択してそれを使って作業を行うか
私が心配しているのは、スレッドプールからスレッドを選択するのではなく、毎回新しいスレッドを作成してメールを送信することです。ますます多くのスレッドを作成し続けると、コンテキストの切り替えによって作成されるオーバーヘッドが原因で、アプリケーションのパフォーマンスが低下します。
.Net framwe work 4.0を使用している場合、魂は非常に簡単になります。System.Collections.Concurrent.ConcurrentQueueを使用して、アイテムのエンキューとデキューを行うことができます。そのスレッドセーフなので、ロックオブジェクトは必要ありません。タスクを使用してメッセージを処理します。
BlockingCollectionは、コンストラクターでIProducerConsumerCollectionを取得します。または、空のコンストラクターを呼び出すと、デフォルトでConcurrentQueueを使用します。
したがって、メッセージをキューに入れます。
//define a blocking collectiom
var blockingCollection = new BlockingCollection<string>();
//Producer
Task.Factory.StartNew(() =>
{
while (true)
{
blockingCollection.Add("value" + count);
count++;
}
});
//consumer
//GetConsumingEnumerable would wait until it find some item for work
// its similar to while(true) loop that we put inside consumer queue
Task.Factory.StartNew(() =>
{
foreach (string value in blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine("Worker 1: " + value);
}
});
アップデート
FrameWork3.5を使用しているため。JosephAlbahariによるConsumer/ProducerQueueの実装をご覧になることをお勧めします。それはあなたがこれまでに見つけたであろう最高の1つです。
上記のリンクから直接コードを取得
public class PCQueue
{
readonly object _locker = new object();
Thread[] _workers;
Queue<Action> _itemQ = new Queue<Action>();
public PCQueue (int workerCount)
{
_workers = new Thread [workerCount];
// Create and start a separate thread for each worker
for (int i = 0; i < workerCount; i++)
(_workers [i] = new Thread (Consume)).Start();
}
public void Shutdown (bool waitForWorkers)
{
// Enqueue one null item per worker to make each exit.
foreach (Thread worker in _workers)
EnqueueItem (null);
// Wait for workers to finish
if (waitForWorkers)
foreach (Thread worker in _workers)
worker.Join();
}
public void EnqueueItem (Action item)
{
lock (_locker)
{
_itemQ.Enqueue (item); // We must pulse because we're
Monitor.Pulse (_locker); // changing a blocking condition.
}
}
void Consume()
{
while (true) // Keep consuming until
{ // told otherwise.
Action item;
lock (_locker)
{
while (_itemQ.Count == 0) Monitor.Wait (_locker);
item = _itemQ.Dequeue();
}
if (item == null) return; // This signals our exit.
item(); // Execute item.
}
}
}
このアプローチの利点は、パフォーマンスを最適化するために作成する必要のあるスレッドの数を制御できることです。スレッドプールアプローチでは、安全ではありますが、同時に作成できるスレッドの数を制御することはできません。