AsParallel()或async / await

时间:2015-01-22 15:23:46

标签: c# .net asynchronous task-parallel-library async-await

让我们假设做一个方法CountString,给定一个字符串数组和一个int,返回长度大于int的字符串数。 如果我必须尽可能地利用多核硬件,那就足够了:

public int CountString(string[] s, int i) 
{
  return s.AsParallel().Count( res => res.length > i);
}

或者我必须以某种方式使用任务甚至混合任务和PLinq?

这只是一个简单的例子,我知道这种方法不会对硬件性能产生太大影响。

我想知道使用AsParallel()是否更好,或者最好是声明方法async并使用await(即使我不喜欢)在方法体中知道如何。

2 个答案:

答案 0 :(得分:7)

修改

当我看到你的实际问题有点误导时,我会尝试回答有意的问题。具体来说,与AsParallel一起使用将是一个很好的方法,因为await实际上并不需要。{1}}。由于您正在处理集合,PLINQ或Paralle.ForEach是一个不错的选择。当您具有自然异步I / O绑定操作时,请考虑使用async-await。建议不要wrap synchronous methods with async wrappers.


如果您实际测试了代码,您甚至可能会惊讶地发现,这段代码并行实际上在方法执行上具有性能成本,具体取决于 size

很多时候人们忘记了使用线程实际上有开销,即使在线程池中使用线程时也是如此。您必须拥有最少量的CPU密集型工作才能获得并行化的性能损失。

如果您的阵列足够长,那么使用AsParallel就足够了。没有理由加入Task,因为PLINQ会很好地处理parallalization。

好的,让我们实际测试一下这段代码。我将迭代一个充满GUID的string[]。这是代码:

主要方法:

void Main()
{
    //JIT
    Test(0);

    Test(100);
    Test(1000);
    Test(10000);
    Test(1000000);
    Test(10000000);
}

public void Test(int itemAmount)
{
    string[] strings = Enumerable.Range(0, itemAmount).Select(i => Guid.NewGuid()
                                                      .ToString()).ToArray();

    var stopWatch = Stopwatch.StartNew();
    CountStringInParallel(strings, itemAmount);
    stopWatch.Stop();
    Console.WriteLine("Parallel Call: String amount: {0}, Time: {1}", 
                                                        itemAmount, stopWatch.Elapsed);

    stopWatch.Restart();
    CountStringSync(strings, itemAmount);
    stopWatch.Stop();
    Console.WriteLine("Synchronous Call: String amount: {0}, Time: {1}", 
                                                        itemAmount, stopWatch.Elapsed);
}

并行和同步:

public int CountStringInParallel(string[] s, int i) 
{
    return s.AsParallel().Count( res => res.Length > i);
}

public int CountStringSync(string[] s, int i) 
{
    return s.Count(res => res.Length > i);
}

结果:

  

并行呼叫:字符串数量:100,时间: 00:00:00.0000197

     

同步通话:字符串数量:100,时间: 00:00:00.0000026


  

并行呼叫:字符串数量:1000,时间: 00:00:00.0000266

     

同步通话:字符串数量:1000,时间: 00:00:00.0000201


  

并行呼叫:字符串数量:10000,时间: 00:00:00.0002060

     

同步通话:字符串数量:10000,时间: 00:00:00.0002003


  

并行呼叫:字符串数量:1000000,时间: 00:00:00.0080492

     

同步通话:字符串数量:1000000,时间: 00:00:00.0135279


  

并行呼叫:字符串数量:10000000,时间: 00:00:00.0744104

     

同步通话:字符串数量:10000000,时间: 00:00:00.1402474

你可以看到最多10,000个字符串,同步方法实际上比并行方法快。

答案 1 :(得分:1)

只要您不使用async-await AsParallel即可。没有理由直接使用任务,因为AsParallel会为你做这件事。

重要的是要记住,并行性有一个开销,在你的情况下可能会大于并行性的附加值。对于实际提高性能的并行性,你应该处理很多项目,而且工作本身应该是非常重要的。

但是,如果您这样做,则需要使用async-await AsParallel(以及PLinq的其余部分),因为它不适合TAP。您需要使用Task.Run来平行处理,并Task.WhenAllawait进行并行处理。与此类似:

var tasks = items.Select(item => Task.Run(() => Process(item));
var results = await Task.WhenAll(tasks);
// process results