【问题标题】:Return "normal" variable from async Task Method从异步任务方法返回“正常”变量
【发布时间】:2020-03-27 14:01:03
【问题描述】:

在我的一种方法中,我想创建这样一辆汽车:

public string[] CreateCar()
{
    string[] wheels = CreateWheels();

    // Also add motor and other stuff here
    string[] motor = ...;

    return new string[][]{ wheels, motor, ... };
}

这里创建了四个轮子,它们彼此不依赖。 因此,我可以将它们放在多个tasks 中,并进行并行化。 (在我的真实代码中,使用并行循环是不可能的)

public string[] CreateWheels()
{
    Task FL = Task.Run(() => CreateWheel("front left");
    Task FR = Task.Run(() => CreateWheel("front right");
    Task BL = Task.Run(() => CreateWheel("back left");
    Task BR = Task.Run(() => CreateWheel("back right");

    // Here I really want to wait for all wheels to be created!
    // STOP THE PROCESS HERE UNTIL ALL WHEELS ARE COMPLETED!!!
    string[] wheels = await Wask.WhenAll(FL, FR, BL, BR); 

    return wheels;
}

一个单独的轮子是这样创建的:

public string CreateWheel(string position)
{
    // Here a wheel is created, whatever it takes :)
    return "wheel " + position;
}

我现在的问题如下:
为了编译代码,它迫使我将CreateWheels 标记为async-Method。否则我不能使用await
⇒ 但这迫使我将其返回值更改为 Task<string[]>
⇒ 但是,然后我需要在CreateCar-Method 中的CreateWheels() 前面放置一个await,以便获取字符串数组而不是任务本身

⇒然后循环重复,因为现在,CreateWheels中有一个await,迫使我使它成为async ...等等,等等...

这个循环一直重复,直到最终到达void 方法,您不需要使用await 提取返回值

如果我只想等待所有轮子在标记点完成,那么这个循环的出路是什么?

【问题讨论】:

  • 除非CreateCar 是一种要求特殊性的方法,否则我不明白您为什么要使用Task.Run 来执行它。
  • string[] wheels = Task.WhenAll(FL, FR, BL, BR).Result; 应该可以工作
  • @UnholySheep:哦,太好了!那么所有任务仍然并行执行吗?
  • 有什么理由不想让代码async all the way down
  • 正如其他人所提到的,异步在某种程度上往往具有“传染性”。根据这段代码的嵌套程度,交换所有内容可能会有点痛苦,但它比尝试调试在 IMO 任务上使用 .Result 可能最终遇到的任何死锁条件更痛苦。跨度>

标签: c# asynchronous async-await task


【解决方案1】:

如果您希望这是同步的,那么Task.WaitAll 可能会有所帮助,即

Task.WaitAll(FL, FR, BL, BR);
var wheels = new[] {FL.Result, FR.Result, BL.Result, BR.Result};

如果您希望这是异步的,则需要创建方法async [Value]Task<string[]>,并在整个过程中传播异步性。异步是有粘性的——它粘在所有东西上;但是:

string[] wheels = await Task.WhenAll(FL, FR, BL, BR);

但是,坦率地说,我怀疑在这种情况下两者都不需要;并行性并不便宜 - 并且可能会增加 很多 比这里值得更多的开销。

【讨论】:

    【解决方案2】:

    可以通过使用Task.Run 启动多个任务来实现并行性,但是为什么不使用像PLINQ 库这样的专用工具来完成这项工作呢?您不仅可以避免使用Task.Result 属性,该属性在与await 结合使用时会导致严重的死锁而臭名昭著,而且您还可以定义最大并行度作为奖励:

    var wheelNames = new[] { "front left", "front right", "back left", "back right" };
    string[] wheels = wheelNames
        .AsParallel()
        .AsOrdered()
        .WithDegreeOfParallelism(4)
        .Select(x => CreateWheel(x))
        .ToArray();
    

    【讨论】:

      【解决方案3】:
      string[] wheels = Wask.WhenAll(FL, FR, BL, BR).Result;
      

      会做你想做的事。这些单独的任务仍将并行运行。

      缺点是你的调用线程会阻​​塞,直到这些任务全部解决。

      【讨论】:

      • 非常感谢!如果以下代码取决于 CreateWheels() 的结果,我的调用线程无论如何都无法避免,对吗?
      • 不使用WhenAll().Result,为什么不使用WaitAll(),因为它是WhenAll的同步对应物?
      • @TylerHundley 两者都可以。 WhenAll 将一次性解开所有任务,并为您留下一个正常的字符串数组。如果您将我的回答与 Marc 的回答进行比较,您会发现即使在调用 WaitAll 之后,他仍然必须处理多个任务并多次调用 Result。但这只是偏好。
      猜你喜欢
      • 1970-01-01
      • 2019-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-12
      • 2016-08-27
      • 2018-10-23
      • 1970-01-01
      相关资源
      最近更新 更多