web-dev-qa-db-ja.com

C#Threadpool-スレッドの数を制限する

コンソールアプリを開発しています。

スレッドプールを使用してWebダウンロードを実行したい。ここにいくつかの偽のコードがあります。

 for (int loop=0; loop< 100; loop++)
 {
     ThreadPool.QueueUserWorkItem(new WaitCallback(GetPage), pageList[loop]);
 }


snip

private static void GetPage(object o)
{
    //get the page
}

コードが2つ(または10など)の同時スレッドを開始しないようにするにはどうすればよいですか?

私が試してみました

    ThreadPool.SetMaxThreads(1, 0);
    ThreadPool.SetMinThreads(1, 0);

しかし、それらは影響がないようです。

20
Bryan

私は使うだろう Parallel.Forとそれに応じてMaxDegreeOfParallelismを設定します。

Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = 10 },
  i =>
  {
    GetPage(pageList[i]);
  });
37
Brian Gideon

ちょうどそのコードを逆に:

ThreadPool.SetMaxThreads(1, 0);
ThreadPool.SetMinThreads(1, 0);

に:

ThreadPool.SetMinThreads(1, 0);
ThreadPool.SetMaxThreads(1, 0);

MaxThreadをMinThreadより小さく設定することはできません

14
Diego Troitiño

個人的には、 SmartThreadPool を使用し、ThreadPoolをそのままにします。ただし、これはおそらくあなたが望むものです: C#スレッドプール制限スレッド

リンクからのコードが含まれています(私ではなく、元の著者のクレジットを入力してください)

System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3);  
try 
{     
  // wait your turn (decrement)     
  S.WaitOne();     
  // do your thing 
}  
finally 
{     
  // release so others can go (increment)     
  S.Release(); 
} 
9
Chris Gessler

説明

これを行うには、ThreadPool.SetMaxThreadsメソッドを使用します。

ただし、WebRequestにThreadPoolを使用する場合、いくつかの問題があります。たとえば、 this(ThreadPoolまたはHttpWebRequestのバグ?) を読み取ります。

サンプル

ThreadPool.SetMaxThreads(2,2);

編集

個人的には、LinqのAsParallelを使用します。

詳しくは

5
dknaack

ThreadPool.SetMaxThreadsのパラメーターを見てください。最初のパラメーターはワーカースレッドの量で、2番目のパラメーターは非同期スレッドの量です。これはあなたが話しているものです。

さらにドキュメントを下に、それは言います:

ワーカースレッドの数またはI/O完了スレッドの数を、コンピューターのプロセッサの数よりも小さい数に設定することはできません。

ThreadPoolを使用するつもりのないものに使用しようとしているようです。ダウンロードの量を制限したい場合は、これを管理するクラスを作成してください。ThreadPoolは必ずしも問題の完全な解決策ではないからです。

ThreadPoolで2つのスレッドを開始し、コールバックを待機するクラスを提案します。スレッドの1つの完了のためのコールバックを受信すると、新しいスレッドをキューに入れます。

2
blockloop

.Net 2.0を使用している場合は、次の手法を使用できます。

ThreadPoolにタスクをキューに入れると、新しいスレッドが作成されるという事実を知っているので(もちろん、空きスレッドがない場合)、空きスレッドができるまで待機します。この目的のために、BlockingCounterクラスが使用されます(以下で説明します)。制限に達すると、誰か(別のスレッド)がデクリメントするまでインクリメントを待機します。次に、「クローズ」状態になり、新しい増分が実行されないことを示し、完了を待ちます。

以下は、合計10の最大4つのタスクを示すサンプルです。

class Program
{

    static int s_numCurrentThreads = 0;

    static Random s_rnd = new Random();

    static void Main(string[] args)
    {

        int maxParallelTasks = 4;
        int totalTasks = 10;

        using (BlockingCounter blockingCounter = new BlockingCounter(maxParallelTasks))
        {
            for (int i = 1; i <= totalTasks; i++)
            {

                Console.WriteLine("Submitting task {0}", i);
                blockingCounter.WaitableIncrement();
                if (!ThreadPool.QueueUserWorkItem((obj) =>
                                                      {
                                                          try
                                                          {
                                                              ThreadProc(obj);
                                                          }
                                                          catch (Exception ex)
                                                          {
                                                              Console.Error.WriteLine("Task {0} failed: {1}", obj, ex.Message);
                                                          }
                                                          finally
                                                          {
                                                              // Exceptions are possible here too, 
                                                              // but proper error handling is not the goal of this sample
                                                              blockingCounter.WaitableDecrement();
                                                          }
                                                      }, i))
                {
                    blockingCounter.WaitableDecrement();
                    Console.Error.WriteLine("Failed to submit task {0} for execution.", i);
                }
            }

            Console.WriteLine("Waiting for copmletion...");
            blockingCounter.CloseAndWait(30000);
        }

        Console.WriteLine("Work done!");
        Console.ReadKey();

    }

    static void ThreadProc (object obj)
    {
        int taskNumber = (int) obj;
        int numThreads = Interlocked.Increment(ref s_numCurrentThreads);

        Console.WriteLine("Task {0} started. Total: {1}", taskNumber, numThreads);
        int sleepTime = s_rnd.Next(0, 5);
        Thread.Sleep(sleepTime * 1000);
        Console.WriteLine("Task {0} finished.", taskNumber);

        Interlocked.Decrement(ref s_numCurrentThreads);
    }

here に投稿されたMarc GravellのSizeQueueに基づくBlockingCounterクラスを使用しますが、キューの代わりにカウンターはありません。新しいスレッドのキューイングを終了したら、Close()メソッドを呼び出して、終了するのを待ちます。

public class BlockingCounter : IDisposable
{
    private int m_Count;
    private object m_counterLock = new object();

    private bool m_isClosed = false;
    private volatile bool m_isDisposed = false;

    private int m_MaxSize = 0;

    private ManualResetEvent m_Finished = new ManualResetEvent(false);

    public BlockingCounter(int maxSize = 0)
    {
        if (maxSize < 0)
            throw new ArgumentOutOfRangeException("maxSize");
        m_MaxSize = maxSize;
    }


    public void WaitableIncrement(int timeoutMs = Timeout.Infinite)
    {
        lock (m_counterLock)
        {
            while (m_MaxSize > 0 && m_Count >= m_MaxSize)
            {
                CheckClosedOrDisposed();
                if (!Monitor.Wait(m_counterLock, timeoutMs))
                    throw new TimeoutException("Failed to wait for counter to decrement.");
            }

            CheckClosedOrDisposed();
            m_Count++;

            if (m_Count == 1)
            {
                Monitor.PulseAll(m_counterLock);
            }

        }
    }

    public void WaitableDecrement(int timeoutMs = Timeout.Infinite)
    {
        lock (m_counterLock)
        {
            try
            {
                while (m_Count == 0)
                {
                    CheckClosedOrDisposed();
                    if (!Monitor.Wait(m_counterLock, timeoutMs))
                        throw new TimeoutException("Failed to wait for counter to increment.");
                }

                CheckDisposed();

                m_Count--;

                if (m_MaxSize == 0 || m_Count == m_MaxSize - 1)
                    Monitor.PulseAll(m_counterLock);
            }
            finally
            {
                if (m_isClosed && m_Count == 0)
                    m_Finished.Set();
            }
        }
    }

    void CheckClosedOrDisposed()
    {
        if (m_isClosed)
            throw new Exception("The counter is closed");
        CheckDisposed();
    }

    void CheckDisposed()
    {
        if (m_isDisposed)
            throw new ObjectDisposedException("The counter has been disposed.");
    }

    public void Close()
    {
        lock (m_counterLock)
        {
            CheckDisposed();
            m_isClosed = true;
            Monitor.PulseAll(m_counterLock);
        }
    }

    public bool WaitForFinish(int timeoutMs = Timeout.Infinite)
    {
        CheckDisposed();
        lock (m_counterLock)
        { 
             if (m_Count == 0)
                 return true;
        }
        return m_Finished.WaitOne(timeoutMs);
    }

    public void CloseAndWait (int timeoutMs = Timeout.Infinite)
    {
        Close();
        WaitForFinish(timeoutMs);
    }

    public void Dispose()
    {
        if (!m_isDisposed)
        {
            m_isDisposed = true;
            lock (m_counterLock)
            {
                // Wake up all waiting threads, so that they know the object 
                // is disposed and there's nothing to wait anymore
                Monitor.PulseAll(m_counterLock);
            }
            m_Finished.Close();
        }
    }
}

結果は次のようになります。

Submitting task 1
Submitting task 2
Submitting task 3
Submitting task 4
Submitting task 5
Task 1 started. Total: 1
Task 1 finished.
Task 3 started. Total: 1
Submitting task 6
Task 2 started. Total: 2
Task 3 finished.
Task 6 started. Total: 4
Task 5 started. Total: 3
Task 4 started. Total: 4
Submitting task 7
Task 4 finished.
Submitting task 8
Task 7 started. Total: 4
Task 5 finished.
Submitting task 9
Task 7 finished.
Task 8 started. Total: 4
Task 9 started. Total: 4
Submitting task 10
Task 2 finished.
Waiting for copmletion...
Task 10 started. Total: 4
Task 10 finished.
Task 6 finished.
Task 8 finished.
Task 9 finished.
Work done!
1
mistika