假设我有以下内容:
public class MyAsyncCode { async Task MethodA() { // Do some stuff... await MethodB(); // Some other stuff } async Task MethodB() { // Do some stuff... await MethodC(); // Some other stuff } async Task MethodC() { // Do some stuff... } }
调用者是同步的(来自控制台应用程序).让我尝试说明我尝试使用Task.WaitAll(…)和包装器任务时要做的事情:
public void MyCallingMethod() { List<Task> tasks = new List<Task>(); for(int c = 0 ; c < 4 ; c++) { MyAsyncCode asyncCode = new MyAsyncCode(); tasks.Add(Task.Run(() => asyncCode.MethodA())); } Task.WaitAll(tasks.ToArray()); }
MethodA,MethodB和MethodC的所有行为都是在连续之前和之后在同一个线程上运行,并且在4个不同的线程上并行发生4次.换句话说,我想删除我的await调用的异步行为,因为我正在从调用者并行调用.
现在,在我进一步讨论之前,我确实理解异步代码和并行/多线程代码之间存在差异,前者并不暗示或暗示后者.我也知道实现此行为的最简单方法是删除async / await声明.不幸的是,我没有选择这样做(它在库中)并且有理由为什么我需要所有的延续都在同一个线程上(与所述库的糟糕设计有关).但更重要的是,这引起了我的兴趣,现在我想从学术角度来了解.
我试图使用PLINQ和使用.AsParallel()立即执行任务来运行它.选择(x => x.MethodA().结果).我还试图使用在这里和那里找到的AsyncHelper类,它实际上只使用.Unwrap().GetAwaiter().GetResult().我也试过其他的东西,我似乎无法得到理想的行为.我要么最终得到同一个线程上的所有调用(显然不是并行),要么以不同线程上执行的连续结束.
我正在尝试做什么,甚至是async / await和TPL太不同了(尽管两者都基于任务)?
解决方法@H_403_20@
您调用的方法不使用ConfigureAwait(false).这意味着我们可以强制继续在我们喜欢的上下文中恢复.选项:
>安装单线程同步上下文.我相信Nito.Async有这个.
>使用自定义TaskScheduler. await查看TaskScheduler.Current并在该调度程序中恢复,如果它是非默认的.
我不确定这两种选择是否有任何利弊.选项2我认为更容易确定范围.选项2看起来像:
Task.Factory.StartNew(
() => MethodA(),new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler).Unwrap();
对每个并行调用调用一次,并使用Task.WaitAll加入所有这些任务.也许你应该处理那个调度程序.
我(ab)在这里使用ConcurrentExclusiveSchedulerPair来获取单线程调度程序.
>安装单线程同步上下文.我相信Nito.Async有这个.
>使用自定义TaskScheduler. await查看TaskScheduler.Current并在该调度程序中恢复,如果它是非默认的.
我不确定这两种选择是否有任何利弊.选项2我认为更容易确定范围.选项2看起来像:
Task.Factory.StartNew( () => MethodA(),new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler).Unwrap();
对每个并行调用调用一次,并使用Task.WaitAll加入所有这些任务.也许你应该处理那个调度程序.
我(ab)在这里使用ConcurrentExclusiveSchedulerPair来获取单线程调度程序.