我目前正努力提高对多线程和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类。
现在,我想知道可以等待并行执行问题的最佳性能版本/最佳实践。 我希望一些更有经验的程序员可以为我阐明这一点!
答案 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()
。