2009-09-10 11 views
3

私はウェブサイトのマルチスレッドスクレーパーに取り組んでいます。異なる質問ごとに、私はQueueUserWorkItem()でThreadPoolを使用することに決めました。どのようにしてQueueUserWorkItemsを連続してキューに入れることなく連続的に行うことができますか?

どのようにして一度にすべての作業項目をキューに入れずにキューに入れることができますか?私は> 300kのアイテム(各userIDに1つ)をキューに入れておく必要があります。もしそれらをキューに入れてループすると、メモリが足りなくなります。

だから、私が希望することです:スレッドが利用可能になると

// 1 = startUserID, 300000 = endUserID, 25 = MaxThreads 
Scraper webScraper = new Scraper(1, 300000, 25); 

webScraper.Start(); 
// return immediately while webScraper runs in the background 

この間、webScraperはcontinuousllyすべて300000の作業項目を追加しています。ここで

は、私がこれまで持っているものです。

public class Scraper 
    { 
     private int MaxUserID { get; set; } 
     private int MaxThreads { get; set; } 
     private static int CurrentUserID { get; set; } 
     private bool Running { get; set; } 
     private Parser StatsParser = new Parser(); 


     public Scraper() 
      : this(0, Int32.MaxValue, 25) 
     { 
     } 

     public Scraper(int CurrentUserID, int MaxUserID, int MaxThreads) 
     { 
      this.CurrentUserID = CurrentUserID; 
      this.MaxUserID = MaxUserID; 
      this.MaxThreads = MaxThreads; 
      this.Running = false; 

      ThreadPool.SetMaxThreads(MaxThreads, MaxThreads); 
     } 

     public void Start() 
     { 
      int availableThreads; 

      // Need to start a new thread to spawn the new WorkItems so Start() will return right away? 
      while (Running) 
      { 

       // if (!CurrentUserID >= MaxUserID) 
       // { 
       //  while (availableThreads > 0) 
       //  { 
       //   ThreadPool.QueueUserWorkItem(new WaitCallBack(Process)); 
       //  } 
       // } 
       // else 
       // { Running = false; } 
      } 
     } 

     public void Stop() 
     { 
      Running = false; 
     } 

     public static void process(object state) 
     { 
      var userID = Interlocked.Increment(ref CurrentUserID); 
      ... Fetch Stats for userID 
     } 
    } 

は、これは正しいアプローチですか?

Start()が呼び出され、一度にすべてのワークアイテムを作成しないと、バックグラウンドで作業アイテムの作成を処理するための正しい方法を誰かが指摘できますか?

+0

あなたはどこかのアイテムをキューに入れる必要があるようです。それらをスレッドプールに作業項目として渡していない場合は、それらをリストに格納して、後でスレッドプールに渡すことができます。あれは正しいですか?そうであれば、スレッドプールに作業を格納すると実際にメモリが消えてしまいますが、自分のリストに格納してもメモリが消えませんか? あなた自身のリストにそれらを保存すると、古いものと古いものがキューに入れられることになります。だから、スレッドプールの最大スレッドの2-3倍のようなものをキューイングし、古いものが終了するとキューを追加します。 –

答えて

2

作業の待ち行列から作業を奪い取る作業項目が少なくて済むように、これを実装する方が良いでしょうか?あなたがそれをする30万の作業をしているからといって、それを行うのに30万人の労働者が必要なわけではありません。明らかにあなたがコアをいくつか持っているだけなので、これらの作品のいくつかだけが並行して起こる可能性があります。だから、ずっと少ない労働者に仕事の塊を渡さないのはなぜですか?

それぞれの作業時間を一定にすることによって、各作業者に均等に分割することも、中央キューをロックすることもできます。それぞれの作業者は作業を取ることができますそれがなくなるにつれて。

EDIT:http://www.bluebytesoftware.com/blog/2008/08/12/BuildingACustomThreadPoolSeriesPart2AWorkStealingQueue.aspx

ジョーダフィーは、ここでキューを盗む仕事を書くことについてのシリーズを持っているようです。それはまた、ネット4のスレッドプールは少しスマートになるだろうのように見えます。しかし、私はあなたがこのシナリオでは特に複雑なものを必要とするとは思わない。

0

私はキューに入れられたアイテムのキューを作成しても何とかしないと思うので、終了したらWorkItemsキューを再作成する方法はありますか?

Startメソッドは、MaxThreadsアイテム(例では75)の3倍をキューに入れ、Processメソッドが完了するとキューに入ります。そうすれば、あなたのStartメソッドは、私は自分自身を発射後、言うように作業項目の数をオフに迅速返しますが、火災:


    public class Scraper 
    { 
     private int MaxUserID { get; set; } 
     private int MaxThreads { get; set; } 
     private int currentUserID; 
     private bool Running { get; set; } 
     private Parser StatsParser = new Parser(); 

     private int Multiplier { get; set; } 

     public Scraper() 
      : this(0, Int32.MaxValue, 25) 
     { 
     } 

     public Scraper(int currentUserID, int maxUserID, int maxThreads) 
     { 
      this.currentUserID = currentUserID; 
      this.MaxUserID = maxUserID; 
      this.MaxThreads = maxThreads; 
      this.Running = false; 

      ThreadPool.SetMaxThreads(maxThreads, maxThreads); 
      Multiplier = 3; 
     } 

     public void Start() 
     { 
      Running = true; 
      for (int i = 0; i < MaxThreads * Multiplier; i++) 
      { 
       ThreadPool.QueueUserWorkItem(Process); 
      } 
     } 

     public void Stop() 
     { 
      Running = false; 
     } 

     public void Process(object state) 
     { 
      if (Running == false) 
      { 
       return; 
      } 
      if (currentUserID < MaxUserID) 
      { 
       Interlocked.Increment(ref currentUserID); 
       //Parse stats for currentUserID 
       ThreadPool.QueueUserWorkItem(Process); 
      } 
      else 
      { Running = false; } 
     } 
    } 

私は実行フラグが安全のためインターロックを使用して設定されるべきであると確信しています。私は乗数をコンストラクタに渡すことができるプロパティにしました - 私はそれがパフォーマンスを調整するために調整することができると確信しています、それらの統計量が解析するのにかかる時間によって異なります。

0

私は間違いなくThreadPool.SetMaxThreadsを使用しません - スレッドプールがすべてのプロセス間で共有されていることを覚えておいてください。 - スレッドの最大量を設定するだけでパフォーマンスが低下します。スレッドプールの背後にあるすべてのアイデアは、スレッドの最大量などを指定する必要はないということです.Net Frameworkは割り振るスレッドの最適量を把握しています。

300,000個のアイテムをキューに入れても、300,000のスレッドが生成されないことに注意してください。ThreadPoolクラスはスレッド数を管理し、必要に応じてスレッドを再利用します。あまりにも多くのリソースがこのように消費されることを心配している場合は、あなたのプロセスを改良することをお勧めします。おそらく1000回のスクレーパーインスタンスを実行する 'Spawner'クラスを作成します。

+0

最初の段落が間違っています。 MSDN(http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx)には、プロセスごとに1つのスレッドプールがあります。 –

+0

更新していただきありがとうございます - 私は間違っていたようです –

0

マスタープロセスコントロールクラスが必要なように見えます。このクラスは、起動しているキューの作業者の量を管理します。

あなたはその後、2つのキューで仕事ができる:

  1. あなたはこのマスター/知事オブジェクトが、その後続けるだろう

仕事をするために

  • セカンドをこすりために必要なすべての項目を保持するための一つのキュー#1のすべてのアイテムがなくなるまでループし、使用可能なサイクルがあるときはキュー#2に追加され続けます。

  • 0

    異なるスレッドプールを使用できます。これは1つです:http://www.codeplex.com/smartthreadpool これで、すべてのアイテムを一度に並べ替えることができます。作成するスレッドの最大数を割り当てることができます。 1000の作業項目があり、100のスレッドを割り当てているとします。それはすぐに最初の100項目を取って、残りが待っている間にそれらを取得します。これらの項目の1つが完了し、スレッドが解放されるとすぐに、次のキュー項目が開始されます。すべての作業を管理しますが、スレッドとメモリを飽和させることはありません。また、.netスレッドプールのスレッドは使用しません。

    関連する問題