更好的模式来启动任务和获取它们的返回值

3

我正在实现一些异步工作,但无法避免地感觉到我的代码变得非常丑陋,我想知道是否可以重写成更好的形式。

 var tasks = new List<Task>();

 var t1 = new Task<Guid>(() => DoSomeStuff<Xyz>(dtx, e1, M1));
 var t2 = new Task<Guid>(() => DoSomeStuff<Qrs>(dtx, e2, M2));
 var t3 = new Task<Guid>(() => DoSomeStuff<Abc>(dtx, e3, M3));

 tasks.Add(t1);
 tasks.Add(t2);
 tasks.Add(t3);

 tasks.ForEach(x => x.Start());

 Task.WaitAll(tasks.ToArray<Task>());

 returnDto.t1value = t1.Result;
 returnDto.t2value = t2.Result;
 returnDto.t3value = t3.Result;

为简洁起见,变量名称已更改,实际上有很多任务。 所有任务都可以独立运行,但必须在我们继续之前完成所有任务。

DoSomeStuff看起来像这样:

private Guid DoSomeStuff<T>(DependentTransaction dtx, T thing, Func<T, Guid> method)

可能更适合在 CodeReview 进行。 - BartoszKP
你能使用 async-await 吗? - Mat J
2个回答

5

使用async-await异步等待会更加容易,使用Task.WhenAll等待多个任务并获取它们的结果,使用Task.Run在并行中运行DoSomeStuff将更加方便:

Guid[] results = await Task.WhenAll(
    Task.Run(() => DoSomeStuff<Xyz>(dtx, e1, M1)),
    Task.Run(() => DoSomeStuff<Qrs>(dtx, e2, M2)), 
    Task.Run(() => DoSomeStuff<Abc>(dtx, e3, M3)));

我认为 Func<T, Guid> 可能会有问题,因为它不知道 awaitability。 - Apeiron
@Apeiron 你的意思是 DoSomeStuff 返回一个 Guid 而不是一个 Task 吗?因为 Task.Run 是返回 Task - i3arnon
重点在于:如果 DoSomeStuff 自然是异步的,那么你应该将其重写为返回 Task<Guid> 并且不再使用 Task.Run。如果它是同步的并且你想要并行化它,那么请使用 Task.Run - i3arnon

1
我正在实现一些异步工作。
重要的是要区分异步和并行工作。异步工作通常基于I/O,而并行工作则使用多个线程来处理CPU密集型代码。
如果您的工作是异步的,则应该有一个异步API可用于将DoSomeStuff变成一个async方法。一旦完成,就相对容易进行并发的异步工作。
Guid[] results = await Task.WhenAll(DoSomeStuffAsync<Xyz>(dtx, e1, M1),
    DoSomeStuffAsync<Qrs>(dtx, e2, M2), DoSomeStuffAsync<Abc>(dtx, e3, M3));

然而,如果你的工作是并行的,那么最好使用Parallel类(或者并行LINQ):

Guid[] results = new Guid[3];
Parallel.Invoke(() => { results[0] = DoSomeStuff<Xyz>(dtx, e1, M1); },
    () => { results[1] = DoSomeStuff<Qrs>(dtx, e2, M2); },
    () => { results[2] = DoSomeStuff<Abc>(dtx, e3, M3); });

在这种情况下,工作明显是I/O绑定的。DoSomeStuff试图将东西插入数据库(这又受到(依赖)事务的控制)。我会研究async/await...尚未尝试过 :) - Apeiron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接