在与同事讨论一段代码时,出现了一些关于其可靠性的问题。具体争论点在于
Task.Run
方法中 Task.WhenAll
和 CreateAxelAsync
的使用。
我就开门见山吧。我对下面的代码有两个问题:
public record Vehicle( Axel FrontAxel, Axel RearAxel ) { }
public record Axel( IList<Wheel> Wheels ) { }
public record Wheel( float TirePressure ) { }
public static class VehicleFactory
{
// async
public static async Task<Vehicle> CreateAsync( int wheelsPerAxel )
{
var frontAxelTask = CreateAxelAsync( wheelsPerAxel );
var rearAxelTask = CreateAxelAsync( wheelsPerAxel );
await Task.WhenAll( frontAxelTask, rearAxelTask );
return new Vehicle( frontAxelTask.Result, rearAxelTask.Result );
}
// async
private static async Task<Axel> CreateAxelAsync( int wheelsPerAxel )
{
var wheelTasks = new Task<Wheel>[ wheelsPerAxel ];
for ( int i = 0; i < wheelsPerAxel; i++ )
{
int wheelIndex = i;
wheelTasks[ i ] = Task.Run( () => CreateWheel( wheelIndex ) );
}
Wheel[] wheels = await Task.WhenAll( wheelTasks );
return new Axel( wheels );
}
// sync
private static Wheel CreateWheel( int wheelIndex )
{
float tirePressure = CalculateTirePressure( wheelIndex );
return new Wheel( tirePressure );
}
// sync
private static float CalculateTirePressure( int wheelIndex )
{
float tirePressure;
// perform some CPU-bound processing and assign to tirePressure
return tirePressure;
}
}
我假设您专门询问这一行,因为它使用
Result
:
return new Vehicle( frontAxelTask.Result, rearAxelTask.Result );
在这种情况下,任务“已经完成”,因此不存在结果陷入僵局的危险。同样,由于任务已经异步完成,因此它不会阻塞异步代码;它一直在异步进行。 也就是说,我(个人)更喜欢使用
await
来提取结果:要么
await
每个任务,要么将 await Task.WhenAll
结果分配给局部变量并从那里获取结果。这是因为,如果删除 WhenAll
,相同的代码也会编译,但它is容易出现死锁,并且does违反异步所有方式。