Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C语言中的并发下载/处理#_C#_Multithreading_Concurrency_Parallel Processing_Task Parallel Library - Fatal编程技术网

C# C语言中的并发下载/处理#

C# C语言中的并发下载/处理#,c#,multithreading,concurrency,parallel-processing,task-parallel-library,C#,Multithreading,Concurrency,Parallel Processing,Task Parallel Library,我正在寻找一种最快、最可靠的方法来同时使用C#下载1000个远程网页(使用HttpWebRequest),将它们写入单个本地文件,并在下载完所有文件后运行一些处理代码,同时充分利用并行性和无阻塞并发性 该服务器是一个运行Windows 2008和.NET 4.0的四核(vCPU)VPS(无法使用较新的异步/等待功能) 你有什么建议 更新:目前提出的选项有:反应式扩展(Rx)、异步CTP、TPL 看起来异步CTP是实现这一点的理想方式,其次是Rx和TPL。伙计们怎么说?VS2010 SP1可以使用

我正在寻找一种最快、最可靠的方法来同时使用C#下载1000个远程网页(使用HttpWebRequest),将它们写入单个本地文件,并在下载完所有文件后运行一些处理代码,同时充分利用并行性和无阻塞并发性

该服务器是一个运行Windows 2008和.NET 4.0的四核(vCPU)VPS(无法使用较新的异步/等待功能)

你有什么建议

更新:目前提出的选项有:反应式扩展(Rx)、异步CTP、TPL


看起来异步CTP是实现这一点的理想方式,其次是Rx和TPL。伙计们怎么说?

VS2010 SP1可以使用异步CTP在.NET 4.0上执行
Async
/
wait
。 VS2012 RC可以使用异步目标包在.NET 4.0上执行
Async
/
wait


但是,如果您确实不想使用
async
/
wait
,您仍然可以使用任务和延续(任务并行库是.NET 4.0的一部分)。

我最近使用C#5的新异步功能和WebLent而不是HttpWebRequest做了类似的事情。您可以使用WebClient获得一些很好的异步方法,例如DownloadDataTaskAsync

WebClient client = new WebClient();
byte[] data = await client.DownloadDataTaskAsync(url)

我会用Rx来完成那个任务

string[] webpages = { "http://www.google.com", "http://www.spiegel.de"};

webpages
    .Select(w => FetchWebPage(w))
    .ForkJoin()
    .Subscribe(x => /*This runs when all webpages have been fetched*/  Console.WriteLine(x));
或者,如果您希望按照svick的建议控制并发性以同时处理最多4个请求,您可以将其更改为:

Observable.ForkJoin(
    webpages
        .Select(w => FetchWebPage(w))
        .Merge(4))
        .Subscribe(x => /*This runs when all webpages have been fetched*/  Console.WriteLine(x));   
您还需要一个助手方法来将常规异步方式转换为Rx方式

public static IObservable<string> FetchWebPage(string address)
{
    var client = new WebClient();

    return Observable.Create<string>(observer =>
    {
        DownloadStringCompletedEventHandler handler = (sender, args) =>
        {
            if (args.Cancelled)
                observer.OnCompleted();
            else if(args.Error != null)
                observer.OnError(args.Error);
            else
            {
                observer.OnNext(args.Result);
                observer.OnCompleted();
            }
        };

        client.DownloadStringCompleted += handler;

        try
        {
            client.DownloadStringAsync(new Uri(address));
        }
        catch (Exception ex)
        {
            observer.OnError(ex);
        }

        return () => client.DownloadStringCompleted -= handler;
    });
}
publicstaticiobservable获取网页(字符串地址)
{
var client=new WebClient();
返回可观察的。创建(观察者=>
{
DownloadStringCompletedEventHandler=(发送方,参数)=>
{
如果(参数已取消)
observer.OnCompleted();
else if(args.Error!=null)
observer.OnError(参数错误);
其他的
{
observer.OnNext(参数结果);
observer.OnCompleted();
}
};
client.DownloadStringCompleted+=处理程序;
尝试
{
DownloadStringAsync(新Uri(地址));
}
捕获(例外情况除外)
{
观察员:OnError(ex);
}
return()=>client.DownloadStringCompleted-=handler;
});
}

我也有类似的需求,但对我来说,URL数量超过7000个(通常需要25-28分钟才能完成)。对于我的解决方案,我使用了TPL。由于每个URL都没有依赖项,因此很容易将每个URL封装在一个对象中,将其放入一个集合中,然后将该集合传递给Parallel.ForEach()调用

每次下载完成后,我们都会查看页面的内容,根据我们找到的内容,我们会将其发送给其他处理

正如我所说,这项工作过去需要半个小时的时间才能完成,但现在只需4.5分钟(我有双四核Xeon处理器@3GHz、Windows 7 Ultimate 64位版本和24 GB RAM……大量的电源正在使用中,而大部分都在浪费)


我对微软的TPL印象深刻,因此我回到了我的大部分遗留项目/代码,并在可能的情况下重构了设计以利用TPL,并且我总是对我编写的任何新代码进行“TPL处理”(如果循环迭代之间存在任何类型的依赖关系,这并不总是可能的).

无论最终使用哪种异步方法,都不要忘记需要增加允许的最大连接数,因为默认值为每个域2个。因此,如果您对单个域进行大量调用,您的速率将仅限于此

您可以使用基本配置在独立(非ASP.NET)应用程序中修复此问题:

<system.net>
   <connectionManagement>
       <add address="*" maxconnections="200" />
   </connectionManagement>
</system.net>

注意:这种基于代码的方法同样适用于非ASP.NET应用程序,因此您可以将其作为“通用”应用程序使用解决方案,如果您想避免.config。

所有这些页面是来自单个网站还是来自1000个不同的网站?@Ramhound我认为WebClient本机不支持多线程。@选择所有页面都来自不同的网站。@Nick WebClient有异步方法,不会占用额外的线程池线程。@Christoph/Ramhound我想您是这样的大家都在引用WebClient异步方法,如下面James Cuthbert的示例所示,这可能是个好消息,但它并没有回答这个问题。特别是因为OP说他不能使用
async
-
wait
。你能修改它来限制并行度吗?因为我不认为同时开始1000次下载是个好主意。如果我没弄错的话,Rx内部使用TPL。看起来TPL对异步操作的控制比Rx更直接。@Nick:Rx使用自己的内部函数(调度器等)。我不相信它是建立在TPL上的。
Observable.ForkJoin()
似乎只出现在Windows Phone的Rx中,而不是正常的Rx中。我说得对吗?那你怎么用普通的Rx来写呢?不,这是最新的测试版。然而,如果缺少ForkJoin替代方案,则可以编写它。我现在在手机上,但下面是如何用RxJS编写它。这和C中的差不多#
ServicePointManager.DefaultConnectionLimit = 200;