2013-03-03 50 views
7

好的在这里我的问题。我想开始线程,直到一定数量。让我们说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

“我不能只是在带有线程池的for循环中启动它们。” - 你真的尝试过吗?假设它会使您的整体互联网连接速度更快,开始多线程听起来并不“合适”。另外Conisder使用异步操作 - 不需要那么多的线程......除非你有类似32核心的机器...... – 2013-03-03 03:44:15

回答

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属性,不限 - Check it out。所以你的代码可能是这样的:

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

现在这很有趣。所以你说这个方法可以用于抓取500k页面。让我试试:) – MonsterMMORPG 2013-03-03 02:23:05

+0

哦。那么它对我来说是没用的:)我开始2000线程来检查活动代理,例如^^,即使任务管理器显示490个线程。我不知道为什么不是2000 :) – MonsterMMORPG 2013-03-03 02:26:39

+0

啊,小心我的编辑 - 最大只有64并行。是的,你可以遍历500,000项目的清单并执行每个项目的func。 – Felix 2013-03-03 02:27:40

0

不是一个确切的答案,但我认为这可能引导你在正确的方向。

首先,看看Thread.Join,特别是在本页底部给出的简单示例。这种方法优于Thread.Sleep(),更适合你的目的。我正在考虑* 行加入 *在“管理器”线程而不是* 睡眠 * ing。

第二个选项可能会或可能不适合您的目的,是新的Tasks库。由于您使用的是最新版本的框架,因此此选项可用,但是我想您无法控制任务库创建的实际线程数。它会根据底层调度程序自动选择该值。但是,有一个名为ParallelOptions.MaxDegreeOfParallelism的选项听起来很有趣。

+0

。我错了吗?如果是这样,我该如何使用它?我不需要等待所有任务。当一个任务完成后,另一个将立即启动,所以总会有一定数量的任务运行 – MonsterMMORPG 2013-03-03 02:21:55

+0

mmmm ...不是100%确定的,但我认为Join只停止调用线程。另一个想法可能是加入新创建的工作线程,以便当其中一个当前正在运行的线程表示它已完成时立即开始工作,因此经理不必一遍又一遍地检查。 – dotNET 2013-03-03 02:28:04

+0

不行,不行。因为线程是独立完成的。首先开始可能会完成最后或最后开始可能会先完成。 – MonsterMMORPG 2013-03-03 02:31:45

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

您正在将任务与线程混合在一起。任务不是一个线程。 There is no guarantee that each task will have it's own thread

其实TPL(Task Parallel Library)是某种队列。这意味着您可以为每个FuncAction对象创建并启动任务。 There is no easy way to control the number of threads实际上已创建。

但是,您可以创建很多任务,而且开销很小,因为TPL会将它们排入队列并应用更多逻辑来平衡thread pool的线程上的工作。

如果需要依次执行某些任务,则可以使用Task.ContinueWith排列它们。也可以使用Task.Factory.ContinueWhenAnyTask.Factory.ContinueWhenAll开始新任务。

这也是您如何控制要创建的并行任务数量的线索:只需创建所需数量的任务并使用ContinueWhenAny排队其余任务即可。每次任务结束时,下一个将开始。

再次说明:TPL将平衡线程池中线程间的工作。无论如何你需要考虑的是使用其他资源,如磁盘I/O或互联网连接。尝试同时使用相同资源的许多任务可能会大大减慢程序的运行速度。

+0

我有很多资源。每秒850 MB I/O读写速度,50 mbit光纤连接。无论如何,这是一些有用的信息投票:) – MonsterMMORPG 2013-03-03 11:53:21