C#使并发执行异步
C# making concurrent executing asynchronous
我目前正在努力提高我对多线程,尤其是 TPL 的理解。
许多构造完全有意义,我可以看到它们如何提高可伸缩性/执行速度。
我知道对于不占用线程的异步调用(如 I/O 绑定调用),Task.WhenAll 是最合适的。
不过,我想知道的一件事是使 CPU 绑定工作的最佳实践,我想 运行 并行异步。
要使代码 运行 并行,显而易见的选择是并行 class。
例如,假设我有一个数据数组,我想执行一些数字 c运行ching on:
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.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 绑定的工作手动创建任务,因为并行 class 应该用于它。
现在我想知道最佳性能版本/最佳实践,以解决希望可以等待的并行执行问题。
我希望一些更有经验的程序员能为我阐明这一点!
选项 1 是可行的方法,因为用于任务的线程池中的线程也将在并行 for 循环中使用。
您真的应该为此使用 Microsoft 的 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 线程,您可以在 .Subscribe
调用之前使用 .ObserveOn
来完成。
如果你想提前停止计算你可以调用subscription.Dispose()
.
我目前正在努力提高我对多线程,尤其是 TPL 的理解。 许多构造完全有意义,我可以看到它们如何提高可伸缩性/执行速度。
我知道对于不占用线程的异步调用(如 I/O 绑定调用),Task.WhenAll 是最合适的。 不过,我想知道的一件事是使 CPU 绑定工作的最佳实践,我想 运行 并行异步。
要使代码 运行 并行,显而易见的选择是并行 class。 例如,假设我有一个数据数组,我想执行一些数字 c运行ching on:
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.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 绑定的工作手动创建任务,因为并行 class 应该用于它。
现在我想知道最佳性能版本/最佳实践,以解决希望可以等待的并行执行问题。 我希望一些更有经验的程序员能为我阐明这一点!
选项 1 是可行的方法,因为用于任务的线程池中的线程也将在并行 for 循环中使用。
您真的应该为此使用 Microsoft 的 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 线程,您可以在 .Subscribe
调用之前使用 .ObserveOn
来完成。
如果你想提前停止计算你可以调用subscription.Dispose()
.