好的,我的问题。我想在一定数量之前启动线程。让我们说100.因此它将开始启动线程并连续检查正在运行的线程数。 当达到最大数量时,它将停止启动新线程。但是通过适当的检查间隔或完成的线程将发出信号,它将启动新的线程。
通过这种方式,我将始终拥有一定数量的正在运行的线程。
我通过使用睡眠和永久性来管理这个。所以我一直用给定的间隔检查总运行线程数,如果线程完成,请将其丢弃并重新开始。
但我的解决方案不是以适当的方式来做我的。我想如果完成的线程发出信号然后检查器会在最大线程数阈值以下的情况下启动一个新的线程会更好。
我看到了很多线程池示例,但大多数都不包含任何排队池,并且运行线程数量最多。我的意思是,他们只是继续启动线程,直到完成。但是让我说我有500k的网址可以收获。我不能在带有线程池的for循环中启动所有这些。
平台是c#4.5 WPF应用程序
以下是我的解决方案。其实我正在寻找一个更好的。没有改进这个。
private void Button_Click_4(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
startCrawler();
});
}
void startCrawler()
{
int irMaximumThreadcount = 100;
List<Task> lstStartedThreads = new List<Task>();
while (true)
{
for (int i = 0; i < lstStartedThreads.Count; i++)
{
if (lstStartedThreads[i].IsCompleted == true)
{
lstStartedThreads[i].Dispose();
lstStartedThreads.RemoveAt(i);
}
}
if (lstStartedThreads.Count < irMaximumThreadcount)
{
var vrTask = Task.Factory.StartNew(() =>
{
func_myTask();
});
lstStartedThreads.Add(vrTask);
}
System.Threading.Thread.Sleep(50);
}
}
void func_myTask()
{
}
答案 0 :(得分:6)
就我个人而言,我会使用PLINQ,特别是WithDegreeOfParallelism方法,它将并发执行的数量限制为传入的值。
private IEnumerable<Action> InfiniteFunctions()
{
while(true)
{
yield return func_myTask;
}
}
private void Button_Click_4(object sender, RoutedEventArgs e)
{
int irMaximumThreadcount = 100;
InfiniteFunctions()
.AsParallel()
.WithDegreeOfParallelism(irMaximumThreadcount)
.ForAll(f => f());
}
编辑:实际上阅读文档似乎irMaximumThreadCount最多只能是64,所以要小心。
编辑2:好的,看起来更好看起来似乎Parallel.ForEach
需要一个ParallelOptions
参数,其中包含MaxDegreeOfParallelism
不受限制的private void CrawlWebsite(string url)
{
//Implementation here
}
private void Button_Click_4(object sender, RoutedEventArgs e)
{
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = 2000
};
Parallel.ForEach(massiveListOfUrls, options, CrawlWebsite);
}
属性 - Check it out。所以你的代码可能就像:
{{1}}
答案 1 :(得分:3)
您正在将任务与线程混合在一起。任务不是线程。 There is no guarantee that each task will have it's own thread
实际上TPL(Task Parallel Library)是某种队列。这意味着您只需为您拥有的每个Func
或Action
对象创建和启动任务。实际创建的There is no easy way to control the number of threads。
但是,您可以创建许多任务而且开销很小,因为TPL会将它们排入队列并应用更多逻辑来平衡thread pool的线程上的工作。
如果需要一个接一个地执行某些任务,您可以使用Task.ContinueWith
将它们排入队列。也可以使用Task.Factory.ContinueWhenAny
或Task.Factory.ContinueWhenAll
开始新任务。
这也是您如何控制要创建的并行任务数量的线索:只需创建所需数量的任务,并使用ContinueWhenAny
将剩余任务排入队列。每次任务结束时,下一个将开始。
再次:TPL将平衡线程池中线程之间的工作。无论如何,您需要考虑的是使用其他资源,如磁盘I / O或互联网连接。尝试同时使用相同资源的许多任务可能会大大减慢您的程序。
答案 2 :(得分:1)
.NET 4.0引入了几个具有内置并发管理的集合,应该是这种情况的理想选择。阻塞集合将更有效,然后在while循环中休眠。然后,您只生成从阻塞队列中读取的x个线程。
BlockingCollection<string> queue = new BlockingCollection<string>(listOfUrls);
for (int x=0; x < MaxThreads; x++)
{
Task.Factory.StartNew(() =>
{
while (true)
{
string url = queue.Take(); // blocks until url is available
// process url;
}
}, TaskCreationOptions.LongRunning);
}
您将任务标记为长时间运行,因此它将创建自己的线程而不是使用线程池。如果您需要先进先出,则可以将ConcurrentQueue<T>
传递给阻塞集合构造函数。 http://msdn.microsoft.com/en-us/library/dd287085.aspx
答案 3 :(得分:0)
不是一个确切的答案,但我认为这可能会指导您正确的方向。
首先,看看Thread.Join,尤其是本页底部给出的简单示例。这种方法优于Thread.Sleep(),更适合您的目的。我正在考虑* 加入 *的“经理”线程,而不是* Sleep * ing。
第二个选项可能适合您的目的,也可能不适合您的目的,是新的Tasks
库。由于您使用的是最新版本的框架,因此该选项可用,但我猜您无法控制Tasks库创建的实际线程数。它会根据基础调度程序自动选择该值。但是,有一个名为ParallelOptions.MaxDegreeOfParallelism的选项听起来很有趣。
答案 4 :(得分:0)
您可以管理自己的任务/线程池,等待任何线程完成并立即启动新线程。
MAX_THREAD_ALLOWED = 100;
List<Task> tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(() => { Foo(i); }));
if (i == MAX_THREAD_ALLOWED)
{
Task.WaitAny(tasks.ToArray());
MAX_THREAD_ALLOWED++;
}
}