C#使并发执行异步

时间:2017-10-22 17:50:20

标签: c# multithreading asynchronous parallel-processing task-parallel-library

我目前正努力提高对多线程和TPL的理解。 很多构造都很有意义,我可以看到它们如何提高可伸缩性/执行速度。

我知道,对于不会占用线程的异步调用(如I / O绑定调用),Task.WhenAll将是最佳选择。 但是,我想知道的一件事是制作CPU绑定工作的最佳实践,我希望以并行异步方式运行。

为了使代码并行运行,显而易见的选择是Parallel类。 举个例子,假设我有一个数据数组,我想对它进行一些数字运算:

string[] arr = { "SomeData", "SomeMoreData", "SomeOtherData" };
Parallel.ForEach(arr, (s) =>
{
    SomeReallyLongRunningMethod(s);
});

这将并行运行(如果分析器确定并行比同步更快),但它也会阻塞线程。

现在我想到的第一件事就是将它全部包装在Task.Run()ala中:

string[] arr = { "SomeData", "SomeMoreData", "SomeOtherData" };
await Task.Run(() => Parallel.ForEach(arr, (s) =>
{
    SomeReallyLongRunningMethod(s);
}));

另一个选择是要么有一个单独的Task returing方法,要么内联它并使用Task.WhenAll就像这样:

static async Task SomeReallyLongRunningMethodAsync(string s)
{
    await Task.Run(() =>
    {
        //work...
    });
}
// ...
await Task.WhenAll(arr.Select(s => SomeReallyLongRunningMethodAsync(s)));

我理解它的方式是选项1创建一个完整的任务,在它的生命周期中,将一个线程绑定到那里并等待Parallel.ForEach完成。 选项2使用Task.WhenAll(我不知道它是否与线程绑定)等待所有任务,但必须手动创建任务。我的一些资源(特别是MS ExamRef 70-483)明确建议不要为CPU绑定工作手动创建任务,因为应该使用Parallel类。

现在,我想知道可以等待并行执行问题的最佳性能版本/最佳实践。 我希望一些更有经验的程序员可以为我阐明这一点!

2 个答案:

答案 0 :(得分:0)

选项1是可行的方法,因为用于该任务的线程池中的线程也将并行用于循环。 Similar question answered here.

答案 1 :(得分:0)

你真的应该使用微软的Reactive Framework。这是完美的解决方案。你可以这样做:

string[] arr = { "SomeData", "SomeMoreData", "SomeOtherData" };

var query =
    from s in arr.ToObservable()
    from r in Observable.Start(() => SomeReallyLongRunningMethod(s))
    select new { s, r };

IDisposable subscription =
    query
        .Subscribe(x =>
        {
            /* Do something with each `x.s` and `x.r` */
            /* Values arrive as soon as they are computed */
        }, () =>
        {
            /* All Done Now */
        });

这假设SomeReallyLongRunningMethod的签名是int SomeReallyLongRunningMethod(string input),但很容易处理其他内容。

它们全部并行运行在多线程上。

如果您需要整理回UI线程,则可以在.ObserveOn来电之前使用.Subscribe执行此操作。

如果您想提前停止计算,可以拨打subscription.Dispose()