时间:2010-07-26 10:59:18

标签: c# multithreading limit threadpool

4 个答案:

答案 0 :(得分:9)

答案 1 :(得分:5)

我通过创建线程并将它们加载到队列中解决了.Net 3.5中的这个问题。然后我从队列中读取一个线程,启动它,并增加正在运行的线程数。我一直这样做,直到达到上限。

当每个线程完成时,它会调用一个回调方法,该方法减少运行计数并通知队列读取器启动更多线程。对于其他控制,您可以使用字典来跟踪由ManagedThreadId键控的正在运行的线程,这样您就可以通知线程提前停止或报告进度。

示例控制台应用:

using System;
using System.Collections.Generic;
using System.Threading;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Supervisor supervisor = new Supervisor();
            supervisor.LaunchThreads();
            Console.ReadLine();
            supervisor.KillActiveThreads();
            Console.ReadLine();
        }

        public delegate void WorkerCallbackDelegate(int threadIdArg);
        public static object locker = new object();

        class Supervisor
        {
            Queue<Thread> pendingThreads = new Queue<Thread>();
            Dictionary<int, Worker> activeWorkers = new Dictionary<int, Worker>();

            public void LaunchThreads()
            {
                for (int i = 0; i < 20; i++)
                {
                    Worker worker = new Worker();
                    worker.DoneCallBack = new WorkerCallbackDelegate(WorkerCallback);
                    Thread thread = new Thread(worker.DoWork);
                    thread.IsBackground = true;
                    thread.Start();
                    lock (locker)
                    {
                        activeWorkers.Add(thread.ManagedThreadId, worker);
                    }
                }
            }

            public void KillActiveThreads()
            {
                lock (locker)
                {
                    foreach (Worker worker in activeWorkers.Values)
                    {
                        worker.StopWork();
                    }
                }
            }

            public void WorkerCallback(int threadIdArg)
            {
                lock (locker)
                {
                    activeWorkers.Remove(threadIdArg);
                    if (activeWorkers.Count == 0)
                    {
                        Console.WriteLine("no more active threads");
                    }
                }
            }
        }

        class Worker
        {
            public WorkerCallbackDelegate DoneCallBack { get; set; }
            volatile bool quitEarly;

            public void DoWork()
            {
                quitEarly = false;
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " started");
                DateTime startTime = DateTime.Now;
                while (!quitEarly && ((DateTime.Now - startTime).TotalSeconds < new Random().Next(1, 10)))
                {
                    Thread.Sleep(1000);
                }
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " stopped");
                DoneCallBack(Thread.CurrentThread.ManagedThreadId);
            }

            public void StopWork()
            {
                quitEarly = true;
            }
        }
    }
}

答案 2 :(得分:1)

处理此问题的最佳方法是仅创建maxDownloads个线程。将所有工作项放入队列中,让线程相互竞争,找出哪个工作项处理每个工作项。

var queue = new ConcurrentQueue<downloadItem>(downloadList);
for (int i = 0; i < Math.Min(maxDownloads, queue.Count))
{
  var thread = new Thread(
    () =>
    {
      while (true)
      {
        downloadItem item = null;
        if (queue.TryDequeue(out item))
        {
          // Process the next work item.
          DownloadItem(item);
        }
        else
        {
          // No more work items are left.
          break;
        }
      }
    });
    thread.IsBackground = true;
    thread.Start();
}

您还可以使用信号量来限制处理工作项的线程数。当实际的线程数未知时,这尤其有用,如果您使用的是ThreadPool

var semaphore = new Semaphore(maxDownloads, maxDownloads);
for (int i = 0; i < downloadList.Count; i++)
{
  downloadItem item = downloadList[i];
  ThreadPool.QueueUserWorkItem(
    (state) =>
    {
      semaphore.WaitOne();
      try
      {
        DownloadItem(item);
      }
      finally
      {
        semaphore.Release();
      }
    });
}

我不是特别喜欢这两种方法。第一个问题是创建了一个非固定数量的线程。通常建议避免在for循环中创建线程,因为它往往不能很好地扩展。第二个问题是信号量将阻塞一些ThreadPool线程。不建议这样做,因为你有效地声称其中一个线程然后什么都不做。这可能会影响恰好共享ThreadPool的其他无关任务的性能。我认为在这种情况下,这两个选项中的任何一个都可以,因为制作一个更具可扩展性的模式更多的是工作而不是它的价值。

答案 3 :(得分:0)