答案 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)