并行vs等待/结果-哪个更适合延迟执行

时间:2018-08-30 06:57:47

标签: c# multithreading

实现#1-使用并行循环

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

实施#2-使用异步方法+结果

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

实现#3-使用异步方法+等待

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

我喜欢使用Parallel循环的实现#1,但是有一个possibility,Parallel会在每次迭代时创建一个新线程,并且会消耗更多资源。

问题

  1. 这些方法之间是否有区别,我可以继续使用Parallel.ForEach吗?
  2. 如果并行循环不好,如何在不创建单独的“异步”方法的情况下对其进行改进?
  3. “ await method.Result”是否与“ await method1 await method2”相同,#2 vs#3?

P.S。因为HttpClient请求数据,然后异步读取,所以有两个“ await”调用。

其他问题-这些行是否相同?

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

1 个答案:

答案 0 :(得分:1)

请考虑以下示例。它可以帮助您找到问题的答案:

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

我建议您仔细阅读以下对您有用的链接:

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

关于问题的最后一部分:

method1.Result.method2();  

调用线程被阻塞,直到method1完成,然后调用method2

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

调用线程未被阻塞,method1正在异步执行。 一旦method1完成,新任务就会像Task.Run(()=> {method1.Result.method2()})一样运行。在这种情况下,method1.Result不会阻塞调用线程,因为method1已完成,isCompleted = true