简体   繁体   中英

Parallel vs Await vs Result - which one is better for deferred execution

Implementation #1 - using Parallel loop

var client = new HttpClient();
var processes = new List<Task<object>>();

Parallel.ForEach(urls, url =>
{
  processes.Add(client.GetAsync(url).Result.Content.ReadAsAsync<object>());
});

Task.WhenAll(processes);

Implementation #2 - using async method + Result

var client = new HttpClient();
var processes = new List<Task<object>>();

urls.ForEach(url =>
{
  processes.Add(GetChain(client, url));
});

Task.WhenAll(processes);

async Task<object> GetChain(HttpClient client, string url)
{
  return await client.GetAsync(url).Result.Content.ReadAsAsync<object>();
}

Implementation #3 - using async method + await

var client = new HttpClient();
var processes = new List<Task<object>>();

urls.ForEach(url =>
{
  processes.Add(GetChain(client, url));
});

Task.WhenAll(processes);

async Task<object> GetChain(HttpClient client, string url)
{
  var chain = await client.GetAsync(url);
  return await chain.Content.ReadAsAsync<object>();
}

I like implementation #1 with Parallel loop, but there is a possibility that Parallel will create a new thread on each iteration and will consume more resources.

Questions

  1. Is there a difference between these methods, can I keep using Parallel.ForEach?
  2. If Parallel loop is bad, how can I improve it without creating a separate "async" method?
  3. Is "await method.Result" the same as "await method1 await method2", #2 vs #3?

PS There are two "await" calls because HttpClient requests data, then read it asynchronously.

Extra question - are these lines the same?

method1.Result.method2                             // get result immediately
method1.ContinueWith(data => data.Result.method2)  // call both methods first

consider the following example . it helps your to find answer on your question:

private static readonly List<Uri> Urls = new List<Uri>() {
    new Uri(""),
    new Uri("") };
....
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var result1 = Urls.Select(GetContent).ToArray();
stopwatch.Stop();
Console.WriteLine($@"Synchronous and NOT In Parallel:{stopwatch.ElapsedMilliseconds}");

stopwatch.Restart();
var result2 = Urls.AsParallel().Select(GetContent).ToArray();
stopwatch.Stop();
Console.WriteLine($@"Synchronous and In Parallel:{stopwatch.ElapsedMilliseconds}");

stopwatch.Restart();
var task1 = DoAsyncNotParallel();
task1.Wait();
stopwatch.Stop();
Console.WriteLine($@"Asynchronous and NOT In Parallel:{stopwatch.ElapsedMilliseconds}");

stopwatch.Restart();
var task2 = DoAsyncInParallel();
task2.Wait();
stopwatch.Stop();
Console.WriteLine($@"Asynchronous and In Parallel:{stopwatch.ElapsedMilliseconds}");

static async Task<string[]> DoAsyncNotParallel()
{
    List<string> content = new List<string>();
    foreach (var uri in Urls)
    {
        content.Add(await GetContentAsync(uri));
    }

    return content.ToArray();
}

static async Task<string[]> DoAsyncInParallel()
{
    var tasks = Urls.Select(uri => GetContentAsync(uri));

    var content = await Task.WhenAll(tasks);

    return content;
}
private static async Task<string> GetContentAsync(Uri uri)
{
    HttpClient httpClient = new HttpClient();
    var response = await httpClient.GetAsync(uri);
    var content = await response.Content.ReadAsStringAsync();
    return content;
}


private static string GetContent(Uri uri)
{
    HttpClient httpClient = new HttpClient();
    var response = httpClient.GetAsync(uri).Result;
    var content = response.Content.ReadAsStringAsync().Result;
    return content;
}

i recommend you to look through the following links which can be useful for you :

  1. Async Programming : Introduction to Async/Await on ASP.NET , by Stephen Cleary
  2. How and Where Concurrent Asynchronous I/O with ASP.NET Web API , by Tugberk Ugurlu
  3. Parallel Programming with .NET , from Microsoft

about last part of question:

method1.Result.method2();  

calling thread is blocked until method1 is completed, then method2 is being called

method1.ContinueWith(data => data.Result.method2());

calling thread is not blocked, method1 is being executed asynchronously . once method1 is completed, then new Task is running like Task.Run(()=>{ method1.Result.method2()}). in this case method1.Result doesn't block calling thread because method1 alread completed, isCompleted=true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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