因此,我将创建一个应用程序来检查链接是否可访问(实时)。我的问题是如何使线程“总是很忙”。我的意思是:该应用程序运行 100 个线程(例如使用 FOR 循环创建)和 100 个不同的 URL。因此,当 1 个线程完成它的工作(检查 URL 是否可用)以获取新 URL 并立即重新开始。所以这 100 个线程将不停地工作,直到检查所有 URL。
我怎样才能做到这一点?
因此,我将创建一个应用程序来检查链接是否可访问(实时)。我的问题是如何使线程“总是很忙”。我的意思是:该应用程序运行 100 个线程(例如使用 FOR 循环创建)和 100 个不同的 URL。因此,当 1 个线程完成它的工作(检查 URL 是否可用)以获取新 URL 并立即重新开始。所以这 100 个线程将不停地工作,直到检查所有 URL。
我怎样才能做到这一点?
您正在寻找的是所谓的Producer-Consumer Model。您有一个资源池,其中包含要检查的 url 列表,一个线程可以填充该池,并且您的消费线程可以从该池中提取,如果您有 .NET 4 Parallel.ForEach为您完成大部分工作。
使用 100 个线程也很可能不是最佳线程数,只需让任务并行库为您管理线程数。
这是一个示例,如果列表将被预先填充并且在线程运行时没有添加更多项目。
//Parallel.Foreach will block until it is done so you may want to run this function on a background worker.
public void StartThreads()
{
List<string> myListOfUrls = GetUrls();
Parallel.Foreach(myListOfUrls, ProcessUrl);
}
private void ProcessUrl(string url)
{
//Do your work here, this code will be run from multiple threads.
}
如果您需要在运行时填充集合,请替换List<string>
为像BlockingCollection这样的并发集合
BlockingCollection<string> myListOfUrls = new BlockingCollection();
//Parallel.Foreach will block until it is done so you may want to run this function on a background worker.
public void StartThreads()
{
if(myListOfUrls.IsComplete == true)
{
//The collection has emptied itself and you told it you where done using it, you will either need to throw a exception or make a new collection.
//use IsCompleatedAdding to check to see if you told it that you are done with it, but there still may be members left to process.
throw new InvalidOperationException();
}
//We create a Partitioner to remove the buffering behavior of Parallel.ForEach, this gives better performance with a BlockingCollection.
var partitioner = Partitioner.Create(myListOfUrls.GetConsumingEnumerable(), EnumerablePartitionerOptions.NoBuffering);
Parallel.ForEach(partitioner, ProcessUrl);
}
public void StopThreads()
{
myListOfUrls.CompletedAdding()
}
public void AddUrl(string url)
{
myListOfUrls.Add(url);
}
private void ProcessUrl(string url)
{
//Do your work here, this code will be run from multiple threads.
}
我还想补充一点,自动线程调度可能也不是最好的,它可能会设置一些可以扩展的限制,请参阅原始问题中的此评论
对于那些说/赞成 100 线程是一个糟糕主意的人:在我的双核 2GB RAM XP 机器上,Parallel.Foreach 从未创建超过 5 个线程(除非我设置了 ThreadPool.SetMinThreads)并且创建 100 个线程总是导致 ~30-40%更快的操作。所以不要把一切都留给 Parallel.Foreach 。PS:我的测试代码 WebClient wc = new WebClient();var s = wc.DownloadString(url); (谷歌主页)- LB
使用 Parallel CTP 的东西,包含的并行 foreach 方法将完全符合您的要求。
谷歌是你的朋友。
此外,使用 100 个线程可能不是最好的性能,但我会使用许多可用的内核。
您可以使用ThreadPool
并为其提供要处理的 url 列表,然后让一个DoWork
方法来检查它们是否处于活动状态,例如
foreach (string s in URLs)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), s);
}
public void DoWork(object sender)
{
string url = (string)sender;
//do stuff with url here
}