2011-08-15 7 views
8

私は、Task内でParallel.ForEachを実行しています。メールアドレスのコレクションを繰り返し処理し、MailMessageをSMTPキューに送信します。送信されると、結果はDB内のテーブルを更新します。Parallel.ForEachコレクションの項目を複数回反復する

私は、MailMessageをキューに複数回、時には最大6回送信していることがDBで分かります。ここでは私の単純化されたコードは、誰もが良いアプローチをお勧めしますか?ボタンのクリックで

、私はこれは、基本的には加入者のConcurrentBagを取得するタスク、(カスタムクラス)を作成し、コレクションを反復処理し、メッセージを送信し

CampaignManager.Broadcast.BroadcastService broadcastService = new CampaignManager.Broadcast.BroadcastService(); 

     var task = Task<CampaignManager.Broadcast.Results.Broadcast>.Factory.StartNew(() => { 
      return broadcastService.BroadcastCampaign(); 
     }, TaskCreationOptions.LongRunning); 

     Task.WaitAny(task); 

     if (task.Result != null) 
     { 
      Broadcast.Results.Broadcast broadcastResult = task.Result; 
      MessageBox.Show(broadcastResult.BroadcastSent.GroupName + " completed. " + broadcastResult.NumberSuccessful + " sent."); 
     } 

...新しいタスクを作成します。.. 。

public Results.Broadcast BroadcastCampaign() 
{ 
// Get ConcurrentBag of subscribers 
subscribers = broadcast.GetSubscribers(); 

// Iterate through subscribers and send them a message 
Parallel.ForEach(subscribers, subscriber => 
{ 
    // do some work, send to SMTP queue 

    // Add to DB log 
}); 

// return result 
} 

IはConcurrentBagさんは、スレッドセーフであるので、それがコレクション内にいくつかの上に複数回反復されるだろう、なぜ私はよく分からないことを信じるように導かいます。 1000のうち、コレクションの10%に対して少なくとも2つのメッセージがキューに入れられます。ありがとう、

Greg。

+0

私はなぜあなたがタスク内で並列を産んでいるのか分かりません。なぜタスクなしでやっていないだけでなく、broadcastService.BroadcastCampaign();を呼び出す –

+0

私はそこにタスクを持っています。最終的には、一度Parallel.ForEach内で仕事をしてしまえば、数秒ごとにブロードキャストサービスを起動するWindowsサービスになります。明らかに仕事が必要です。それはタスクの中で実行されていたのであって、最終コードではありませんでした。 – gfyans

答えて

6

私は、ConcurrentBagはスレッドセーフであると考えられているので、何回コレクション内で何回繰り返すのかはわかりません。

あなたの前提は当てはまります。実際にはConcurrentBag<T>GetEnumerator<T>メソッド(コレクションを列挙するため)は、実際にその時点で内部コレクションのコピー全体を作成するので、コレクションのコピーを反復処理しています。

あなたは単一の加入者のために複数回呼び出されているキューを見ている場合、それはあなたがConcurrentBag<T>複数回にその加入者を追加したことを意味し、または起こっている他のいくつかの問題がある...


別の注記では、ここでのタスクの使用は実際には必要ありません。これはオーバーヘッドを追加するだけです(この場合は専用のスレッドを作成し、すぐにブロックして待機します)。ただ、すぐにそれを待つようにタスクの作成

CampaignManager.Broadcast.BroadcastService broadcastService = new CampaignManager.Broadcast.BroadcastService(); 

Broadcast.Results.Broadcast broadcastResult = broadcastService.BroadcastCampaign(); 
MessageBox.Show(broadcastResult.BroadcastSent.GroupName + " completed. " + broadcastResult.NumberSuccessful + " sent."); 

Task.WaitAny)まったく役に立たない:とてもとしてだけで、あなたのメソッドを呼び出すために、これを書き換えるためにはるかに良いでしょう。さらに、Task.WaitAny(...)を使用する代わりに、他の目的のためにタスクを実行したい場合は、broadcastResult = task.Result;を呼び出すことができます。これは、タスクが完了するまでブロックされるためです。

+0

シンプルなforまたはforeachを使用するように変更すると、間違いなく他の問題が起こっていると思います。加入者ごとに1つのメッセージを送信するだけです(ちょうど2倍の時間がかかります)。私は明日の周りのコードを変更し、タスクを取り除き、ConcurrentBagをIEnemurableに変更します(それがどういうことであれ)。報告する。 – gfyans

+0

@Greg F:私は、あなたの "do some work"が内部的にスレッドセーフではないと思っています... –

+0

はい、そうです、それはスレッドセーフではない作業でした!今朝改めて仕事をしてください。ご協力いただきありがとうございます。 – gfyans