web-dev-qa-db-fra.com

c # Threadpool - limite le nombre de threads

Je développe une application console.

Je souhaite utiliser un Threadpool pour effectuer des téléchargements Web. Voici du faux code.

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


snip

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

Comment empêcher mon code de démarrer plus de deux (ou dix, ou autre) threads simultanés?

J'ai essayé

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

Mais ils semblent n'avoir aucun impact.

20
Bryan

J'utiliserais Parallel.For et définissez MaxDegreeOfParallelism en conséquence.

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

Inversez simplement ce code de:

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

À:

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

Vous ne pouvez pas définir le MaxThread plus petit que MinThread

14
Diego Troitiño

Personnellement, j'utiliserais SmartThreadPool et laisserais le ThreadPool tranquille. Cependant, c'est probablement ce que vous voulez: threads limitant le pool de threads C #

Code inclus depuis le lien (merci de donner à l'auteur d'origine, pas moi)

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

La description

Vous pouvez le faire en utilisant le ThreadPool.SetMaxThreads méthode.

Mais il existe certains problèmes lors de l'utilisation de ThreadPool pour WebRequest. Lisez, par exemple, this (Bug dans ThreadPool ou HttpWebRequest?)

Échantillon

ThreadPool.SetMaxThreads(2,2);

Éditer

Personnellement, j'utiliserais AsParallel de Linq, pour cela.

Plus d'information

5
dknaack

Regardez les paramètres de ThreadPool.SetMaxThreads. Le premier paramètre est la quantité de threads de travail et le second paramètre est la quantité de threads asynchrones, dont vous parlez.

Plus bas dans la documentation, il est écrit:

Vous ne pouvez pas définir le nombre de threads de travail ou le nombre de threads d’achèvement des E/S sur un nombre inférieur au nombre de processeurs de l’ordinateur.

Il semble que vous essayez d'utiliser le ThreadPool pour quelque chose pour lequel il n'est pas destiné à être utilisé. Si vous souhaitez limiter le nombre de téléchargements, créez une classe qui gère cela pour vous, car le ThreadPool n'est pas nécessairement la solution complète à votre problème.

Je suggère une classe qui démarre deux threads dans le ThreadPool et attend le rappel. Lorsqu'il reçoit un rappel pour l'achèvement de l'un des threads, en file d'attente un nouveau.

2
blockloop

Si vous serrez à .Net 2.0, vous pouvez utiliser la technique suivante:

sachant que si vous mettez en file d'attente une tâche dans le ThreadPool cela créera un nouveau thread (bien sûr s'il n'y en a pas de libre), vous attendrez avant de faire cela jusqu'à ce qu'il y ait un thread libre. À cet effet, la classe BlockingCounter est utilisée (décrite ci-dessous) qui, une fois la limite atteinte, attendra pour s'incrémenter jusqu'à ce que quelqu'un (un autre thread) la décrémente. Il est ensuite entré dans l'état "fermé" indiquant qu'aucun nouvel incrément ne sera effectué et attend la fin.

Voici l'exemple qui montre un maximum de 4 tâches avec un nombre total de 10.

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);
    }

Il utilise la classe BlockingCounter qui est basée sur SizeQueue de Marc Gravell publiée ici , mais sans compteur au lieu d'une file d'attente. Lorsque vous terminez la mise en file d'attente de nouveaux threads, appelez la méthode Close (), puis attendez qu'elle se termine.

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();
        }
    }
}

Le résultat sera comme ça:

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