【发布时间】:2016-06-29 00:12:43
【问题描述】:
我正在开发一个 asp.net MVC-5 web 应用程序,根据我读到的一些文章,我不应该在 web 服务器和 .net web 应用程序中使用并行方法。
现在就我而言,我需要在 foreach 中发出大约 1,500 个 WebClient() 调用,然后反序列化来自 WebClient() 调用的返回 json 对象。我在使用 Parallel.Foreach 之前的原始代码如下,大约需要 15 分钟才能完成:-
public async Task <List<Details2>> Get()
{
try
{
using (WebClient wc = new WebClient())
{
string url = currentURL + "resources?AUTHTOKEN=" + pmtoken;
var json = await wc.DownloadStringTaskAsync(url);
resourcesinfo = JsonConvert.DeserializeObject<ResourcesInfo>(json);
}
ForEach( var c in resourcesinfo.operation.Details)
{
ResourceAccountListInfo resourceAccountListInfo = new ResourceAccountListInfo();
using (WebClient wc = new WebClient())
{
string url = currentURL + "resources/" + c.RESOURCEID + "/accounts?AUTHTOKEN=" + pmtoken;
string tempurl = url.Trim();
var json = await wc.DownloadStringTaskAsync(tempurl);
resourceAccountListInfo = JsonConvert.DeserializeObject<ResourceAccountListInfo>(json);
}
if (resourceAccountListInfo.operation.Details.CUSTOMFIELD.Count > 0)
{
List<CUSTOMFIELD> customfield = resourceAccountListInfo.operation.Details.CUSTOMFIELD.Where(a =>
a.CUSTOMFIELDLABEL.ToLower() == "name"
).ToList();
if (customfield.Count == 1)
{
PMresourcesOnly.Add(resourceAccountListInfo.operation.Details);
}
}
}//end of foreach
return PMresourcesOnly.ToList();
}
catch (Exception e)
{
}
return new List<Details2>();
}
现在我做了以下修改:-
- 我将
foreach替换为Parallel.ForEach -
因为我不应该在
Parallel.ForEach中使用异步方法,所以我将DownloadStringTaskAsync中的DownloadString更改为Parallel.Foreach中的DownloadString:-public async Task <List<Details2>> Get() { try { using (WebClient wc = new WebClient()) { string url = currentURL + "resources?AUTHTOKEN=" + pmtoken; var json = await wc.DownloadStringTaskAsync(url); resourcesinfo = JsonConvert.DeserializeObject<ResourcesInfo>(json); } Parallel.ForEach(resourcesinfo.operation.Details, new ParallelOptions { MaxDegreeOfParallelism = 7 }, (c) => { ResourceAccountListInfo resourceAccountListInfo = new ResourceAccountListInfo(); using (WebClient wc = new WebClient()) { string url = currentURL + "resources/" + c.RESOURCEID + "/accounts?AUTHTOKEN=" + pmtoken; string tempurl = url.Trim(); var json = wc.DownloadString(tempurl); resourceAccountListInfo = JsonConvert.DeserializeObject<ResourceAccountListInfo>(json); } if (resourceAccountListInfo.operation.Details.CUSTOMFIELD.Count > 0) { List<CUSTOMFIELD> customfield = resourceAccountListInfo.operation.Details.CUSTOMFIELD.Where(a => a.CUSTOMFIELDLABEL.ToLower() == "name" ).ToList(); if (customfield.Count == 1) { PMresourcesOnly.Add(resourceAccountListInfo.operation.Details); } } });//end of foreach return PMresourcesOnly.ToList(); } catch (Exception e) { } return new List<Details2>(); }
现在,当我使用 Parallel.Foreach 时,执行时间从 15 分钟减少到大约 7 分钟。但是如果我的第二种方法有效,我会有点困惑,所以任何人都可以就这些问题(或任何问题)提出建议:-
使用
Parallel.Foreach和Webclient()是一种有效的方法吗?或者我应该避免在 .net 和 Web 应用程序中使用并行方法?在使用
Parallel.Foreach时,我是否会遇到任何问题,例如return PMresourcesOnly.ToList();已返回给客户端,而仍有一些wc.DownloadString(tempurl);未完成?如果我想比较 2 种方法(Parallel.Foreach 和 Foreach),结果是否相同?
在一些在线文章中,他们使用
Task.Factory.StartNew(()而不是使用Parallel.foreach,那么它们之间的主要区别是什么?
编辑
我尝试将SemaphoreSlim 定义如下:-
public async Task <List<Details2>> Get()
{
SemaphoreSlim throttler = new SemaphoreSlim(initialCount: 15);
try
{
//code goes here
var tasks = resourcesinfo.operation.Details.Select(c => TryDownloadResourceAsync(c.RESOURCEID,throttler)).ToList();
}
///---
private async Task<Details2> TryDownloadResourceAsync(string resourceId, SemaphoreSlim throttler)
{
await throttler.WaitAsync();
try
{
using (WebClient wc = new WebClient()) //get the tag , to check if there is a server with the same name & tag..
{}
}
finally
{
throttler.Release();
}
【问题讨论】:
-
Parallel.ForEach 是正确的方法。在您的情况下使用起来更容易。您也可以使用 Task.Factory.StartNew 但您还必须添加“等到全部完成”。这就是为什么 Parallel.ForEach 更容易使用的原因。我还将 Parallel.ForEach 与 WebClient 一起使用。如果您使用 Parallel,请注意同步您的代码。不要忘记使用锁。
-
@Stanley 您能否就“如果您使用 Parallel,那么请注意同步您的代码。不要忘记使用锁定”提供更多建议。就我而言,我在 paralle.foreach 中使用了同步方法,但是我应该在哪里放置锁以及为什么要使用锁?
-
PMresourcesOnly.Add(resourceAccountListInfo.operation.Details);必须锁定。您正在从多个线程添加到集合中。看到这个:msdn.microsoft.com/en-us/library/c5kehkcz.aspx
-
@Stanley 那么如果我向其中添加来自多个线程的集合有什么问题???不知道使用锁的想法是什么?
标签: c# asp.net .net asp.net-mvc parallel-processing