限制线程计数

我有一个列表,其中包含我要下载的项目。 我使用for循环来迭代列表。

对于此List中的每个项目,我启动一个引用该项目的新线程。 我的问题是我想要同时限制maxDownload。

for (int i = downloadList.Count - 1; i >= 0; i--) { downloadItem item = downloadList[i]; if (item.Status != 1 && item.Status != 2) { ThreadStart starter = delegate { this.DownloadItem(ref item); }; Thread t = new Thread(starter); t.IsBackground = true; t.Name = item.Name; t.Priority = ThreadPriority.Normal; t.Start(); } } 

我读了一些关于ThreadPool的内容,但后来我无法引用我的项目。 有人能帮我吗? 谢谢! 🙂

编辑:

我测试了这个:

 ThreadPool.SetMaxThreads(maxDownloads, maxDownloads); ThreadPool.SetMinThreads(maxDownloads, maxDownloads); ThreadPool.QueueUserWorkItem(DownloadItem, ref item); 

我不知道如何用这个post引用我的downloadItem …..

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

当每个线程完成时,它会调用一个回调方法,该方法减少运行计数并通知队列读取器启动更multithreading。 对于其他控件,您可以使用字典来跟踪由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 pendingThreads = new Queue(); Dictionary activeWorkers = new Dictionary(); 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; } } } } 

如果你使用的是.NET 4,我强烈建议使用Parallel.ForEach (可能在downloadList.Reverse()上)

所以,像:

 Parallel.ForEach(downloadList.Reverse(), new ParallelOptions { MaxDegreeOfParallelism = 8 }, item => this.DownloadItem(item)); 

如果你不想阻塞调用线程,你当然可以使用QueueUserWorkItem这个调用。

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

 var queue = new ConcurrentQueue(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的其他无关任务的性能。 我认为在这种情况下,两个选项中的任何一个都可以,因为制作一个更具可伸缩性的模式更多的是工作而不是它的价值。

我无法理解你为什么要尝试使用ref关键字。 默认情况下,对象在C#中通过引用传递,在原始代码中,在传递给DownloadItem之后,您不使用item 。 因此,我建议使用您尝试过的ThreadPool方法,但不要使用ref参数。

希望有所帮助。