在我的asp.net mvc Web应用程序

时间:2016-06-29 00:12:43

标签: c# asp.net .net asp.net-mvc parallel-processing

我正在开发一个asp.net MVC-5 Web应用程序,基于我读过的一些文章,我不应该在Web服务器和.net web应用程序中使用并行方法。 现在在我的情况下,我有大约1,500个WebClient()调用,我需要在foreach中发出,然后从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: -

    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 分钟左右。但如果我的第二种方法有效,我会有点困惑,所以任何人都可以对这些问题(或任何问题)进行思考: -

  1. 正在使用Parallel.ForeachWebclient()采用有效方法吗?或者我应该避免在.net和Web应用程序中使用Parallel方法吗?

  2. 使用Parallel.Foreach时,我是否会遇到任何问题,例如return PMresourcesOnly.ToList();会返回客户端,而仍有一些wc.DownloadString(tempurl);未完成?

  3. 如果我想比较两种方法(Parallel.Foreach&amp; Foreach),结果是否相同?

  4. 在一些在线文章中,他们使用Task.Factory.StartNew(()而不是Parallel.foreach,那么它们之间的主要区别是什么?

  5. 修改 我尝试将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();
                }
    

2 个答案:

答案 0 :(得分:7)

  

正在使用Parallel.Foreach和Webclient()一个有效的方法吗?或者我应该避免在.net和Web应用程序中使用Parallel方法吗?

不,你绝对应该避免在ASP.NET应用程序中使用并行方法。

  

在一些在线文章中,他们使用Task.Factory.StartNew(()而不是使用Parallel.foreach,那么它们之间的主要区别是什么?

Parallel用于data parallism(在数据项集合上运行相同的CPU绑定代码)。 StartNew用于dynamic task parallelism(在处理项目的集合上运行相同或不同的CPU绑定代码)。

这两种方法都不合适,因为你需要做的工作是I / O绑定,而不是CPU绑定。

你真正想要的是并发(一次做多件事),而不是 parallelism 。而不是使用并行并发(通过使用多个线程一次做多个事情),你想要的是异步并发(使用无线程一次做多件事)。

通过await Task.WhenAll在代码中可以实现异步并发:

private async Task<string> TryDownloadResourceAsync(string resourceId)
{
  ResourceAccountListInfo resourceAccountListInfo = new ResourceAccountListInfo();
  using (WebClient wc = new WebClient()) 
  {
    string url = currentURL + "resources/" + 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)
    {
      return resourceAccountListInfo.operation.Details;
    }
  }
  return null;
}

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

    var tasks = resourcesinfo.operation.Details.Select(c => TryDownloadResourceAsync(c.RESOURCEID)).ToList();
    var results = await Task.WhenAll(tasks).Select(x => x != null);
    return results.ToList();
  }
  catch (Exception e)
  {
  }
  return new List<Details2>(); // Please, please don't do this in production.
}

作为最后一点,您可能需要查看专为异步操作而设计的HttpClient,并且具有良好的属性,您只需要一个来实现任意数量的同步调用

答案 1 :(得分:0)

看看:
object syncObj = new object();
锁(syncObj)

try
{
    using (WebClient wc = new WebClient()) 
    {
        string url = currentURL + "resources?AUTHTOKEN=" + pmtoken;
        var json = await wc.DownloadStringTaskAsync(url);
        resourcesinfo = JsonConvert.DeserializeObject<ResourcesInfo>(json);
    }

    object syncObj = new object();  // create sync object
    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);
        }

        lock(syncObj)  // lock using sync object
        {
            PMresourcesOnly.Add(resourceAccountListInfo.operation.Details);
        }
    });//end of foreach

    return PMresourcesOnly.ToList();
}
catch (Exception e)
{
}