简体   繁体   English

在我的asp.net mvc Web应用程序中使用带有WebClient()的Parallel.Foreach有任何缺点或风险吗?

[英]are there any drawbacks or risks of using Parallel.Foreach with WebClient() inside my asp.net mvc web application

I am working on an asp.net MVC-5 web application, and based on some articles i read that i should not use Parallel methods inside web servers and inside .net web applications espically. 我正在开发一个asp.net MVC-5 Web应用程序,基于我读过的一些文章,我不应该在Web服务器和.net web应用程序中使用并行方法。 now in my case i have around 1,500 WebClient() calls that i need to issue inside a foreach, and then deserialize the return json object from the WebClient() calls. 现在在我的情况下,我有大约1,500个WebClient()调用,我需要在foreach中发出,然后从WebClient()调用反序列化返回json对象。 my original code before using Parallel.Foreach was as follow, which took around 15 minutes to complete:- 使用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>();
            }

now i did the following modifications :- 现在我做了以下修改: -

  • i replace foreach with Parallel.ForEach 我用Parallel.ForEach替换foreach
  • since i should not use async methods inside Parallel.ForEach so i chnage the DownloadStringTaskAsync to DownloadString inside the Parallel.Foreach :- 因为我不应该使用内异步方法Parallel.ForEach ,所以我chnage的DownloadStringTaskAsyncDownloadString里面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>(); } 

now when i use the Parallel.Foreach the execution time was reduced from 15 minutes to around 7 minutes. 现在,当我使用Parallel.Foreach ,执行时间从15分钟减少到大约7分钟。 but i am a bit confused if my second method is valid , so can anyone adivce on these questions (or any question):- 但如果我的第二种方法有效,我会有点困惑,所以任何人都可以对这些问题(或任何问题)进行思考: -

  1. is using Parallel.Foreach with Webclient() a valid approach to follow ? 正在使用Parallel.ForeachWebclient()一个有效的方法来遵循? or i should avoid using Parallel methods inside .net and web applications? 或者我应该避免在.net和Web应用程序中使用Parallel方法?

  2. when using Parallel.Foreach could i face any problem such as that the return PMresourcesOnly.ToList(); 当使用Parallel.Foreach ,我可以遇到任何问题,例如return PMresourcesOnly.ToList(); is return to the client while there are still some wc.DownloadString(tempurl); 返回客户端时仍然有一些wc.DownloadString(tempurl); that did not complete? 那还没完呢?

  3. if i want to compare the 2 methods (Parallel.Foreach & Foreach) will the result be the same ? 如果我想比较2种方法(Parallel.Foreach&Foreach),结果是否相同?

  4. on some online articles they use Task.Factory.StartNew(() instead of using Parallel.foreach so what are the main differences between them ? 在一些在线文章中,他们使用Task.Factory.StartNew(()而不是使用Parallel.foreach那么它们之间的主要区别是什么?

EDIT I tried defining the SemaphoreSlim as follow:- 编辑我尝试定义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();
            }

is using Parallel.Foreach with Webclient() a valid approach to follow ? 正在使用Parallel.Foreach和Webclient()一个有效的方法来遵循? or i should avoid using Parallel methods inside .net and web applications? 或者我应该避免在.net和Web应用程序中使用Parallel方法?

No, you absolutely should avoid using parallel methods inside ASP.NET apps. 不,你绝对应该避免在ASP.NET应用程序中使用并行方法。

on some online articles they use Task.Factory.StartNew(() instead of using Parallel.foreach so what are the main differences between them ? 在一些在线文章中,他们使用Task.Factory.StartNew(()而不是使用Parallel.foreach,那么它们之间的主要区别是什么?

Parallel is for data parallism (running the same CPU-bound code over a collection of data items). Parallel用于数据并行 (在数据项集合上运行相同的CPU绑定代码)。 StartNew is for dynamic task parallelism (running the same or different CPU-bound code over a collection of items that changes as you process it). StartNew用于动态任务并行 (在处理它时更改的项集合上运行相同或不同的CPU绑定代码)。

Neither approach is appropriate here, since the work you have to do is I/O-bound, not CPU-bound. 这两种方法都不合适,因为您需要做的工作是I / O绑定,而不是CPU绑定。

What you actually want is concurrency (doing multiple things at a time), not parallelism . 你真正想要的是并发性 (一次做多件事),而不是并行性 Instead of using parallel concurrency (doing multiple things at a time by using multiple threads), what you want is asynchronous concurrency (doing multiple things at a time using no threads). 而不是使用并行并发 (通过使用多个线程一次做多个事情),你想要的是异步并发 (使用无线程一次做多件事)。

Asynchronous concurrency is possible in code via await Task.WhenAll , as such: 代码中的异步并发可以通过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.
}

As a final note, you may want to look into HttpClient , which was designed for asynchronous operations and has the nice property that you only need one of them for any number of simultaneous calls. 最后要注意的是,您可能需要查看HttpClient ,它是专为异步操作而设计的,并且具有良好的属性,您只需要其中一个用于任意数量的同时调用。

Take a look at: 看一眼:
object syncObj = new object(); object syncObj = new object();
lock(syncObj) 锁(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)
{
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM