lambdas () => DoSomethingAsync()
和async () => await DoSomethingAsync()
两者都被列为Func<Task>
时有区别吗?我们应该选择哪一个?何时?
这是一个简单的控制台应用
using System;
using System.Threading.Tasks;
namespace asyncDemo
{
class Program
{
static void Main(string[] args)
{
var demo = new AsyncDemo();
var task = demo.RunTheDemo();
task.Wait();
Console.ReadLine();
}
}
public class AsyncDemo
{
public async Task Runner(Func<Task> action)
{
Console.WriteLine(DateTime.Now.ToLongTimeString() + " Launching the action");
await action();
}
private async Task DoSomethingAsync(string suffix)
{
await Task.Delay(2000);
Console.WriteLine(DateTime.Now.ToLongTimeString() + " Done something, " + suffix);
}
public async Task RunTheDemo()
{
await Runner(() => DoSomethingAsync("no await"));
await Runner(async () => await DoSomethingAsync("with await"));
}
}
}
输出结果为:
09:31:08 Launching the action
09:31:10 Done something, no await
09:31:10 Launching the action
09:31:12 Done something, with await
所以在RunTheDemo
中,对await Runner(someLambda);
的两次调用似乎都采用相同的时序特性做同样的事情 - 两者都有正确的两秒延迟。
这两条线都有效,它们完全相同吗? () => DoSomethingAsync()
和async () => await DoSomethingAsync()
结构之间有什么区别?我们应该选择哪一个?何时?
这与“我应该在一般情况下使用await
”这个问题不同,因为我们正在处理工作异步代码,其中lambdas键入为Func<Task>
,这些内容正在等待消费方法。问题涉及如何宣布这些lambda,以及该声明的效果是什么。
答案 0 :(得分:20)
使用和不使用异步
声明的lambda之间是否存在差异
是的,有区别。一个是异步lambda,另一个是返回任务的lambda。
async lambda被编译成状态机,而另一个并不是这样,因为异步lambda具有不同的异常语义,因为异常被封装在返回的任务中并且不能同步抛出。
它与常规方法完全相同。例如,在此异步方法之间:
public class Door : MonoBehaviour
{
public float ySensitivity = 300f;
public float frontOpenPosLimit = 45;
public float backOpenPosLimit = 45;
public GameObject frontDoorCollider;
public GameObject backDoorCollider;
bool moveDoor = false;
DoorCollision doorCollision = DoorCollision.NONE;
// Use this for initialization
void Start()
{
StartCoroutine(doorMover());
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Mouse down");
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo))
{
if (hitInfo.collider.gameObject == frontDoorCollider)
{
moveDoor = true;
Debug.Log("Front door hit");
doorCollision = DoorCollision.FRONT;
}
else if (hitInfo.collider.gameObject == backDoorCollider)
{
moveDoor = true;
Debug.Log("Back door hit");
doorCollision = DoorCollision.BACK;
}
else
{
doorCollision = DoorCollision.NONE;
}
}
}
if (Input.GetMouseButtonUp(0))
{
moveDoor = false;
Debug.Log("Mouse up");
}
}
IEnumerator doorMover()
{
bool stoppedBefore = false;
float yRot = 0;
while (true)
{
if (moveDoor)
{
stoppedBefore = false;
Debug.Log("Moving Door");
yRot += Input.GetAxis("Mouse Y") * ySensitivity * Time.deltaTime;
//Check if this is front door or back
if (doorCollision == DoorCollision.FRONT)
{
Debug.Log("Pull Down(PULL TOWARDS)");
yRot = Mathf.Clamp(yRot, -frontOpenPosLimit, 0);
Debug.Log(yRot);
transform.localEulerAngles = new Vector3(0, -yRot, 0);
}
else if (doorCollision == DoorCollision.BACK)
{
Debug.Log("Pull Up(PUSH AWAY)");
yRot = Mathf.Clamp(yRot, 0, backOpenPosLimit);
Debug.Log(yRot);
transform.localEulerAngles = new Vector3(0, yRot, 0);
}
}
else
{
if (!stoppedBefore)
{
stoppedBefore = true;
Debug.Log("Stopped Moving Door");
}
}
yield return null;
}
}
enum DoorCollision
{
NONE, FRONT, BACK
}
}
这个任务返回方法:
async Task FooAsync()
{
await DoSomethingAsync("with await");
}
观察这些方法可以更清楚地显示差异,但由于lambda只是语法糖,实际上编译成与这些方法相同的方法。
我们应该选择哪一个?何时?
这真的取决于你的口味。使用async关键字会生成一个状态机,其性能不如简单地返回任务。但是,在某些情况下,异常语义可能会令人惊讶。
以此代码为例:
Task FooAsync()
{
return DoSomethingAsync("no await");
}
try-catch块是否会处理Hamster hamster = null;
Func<Task> asyncAction = () => FooAsync(hamster.Name);
var task = asyncAction();
try
{
await task;
}
catch
{
// handle
}
?
它不会因为在调用NullReferenceException
时同步抛出异常。但是,在这种情况下,将处理异常,因为它在返回的任务中被捕获,并在等待该任务时重新抛出。
asyncAction
我个人使用返回任务的lambda来表示这一行表达式lambdas,因为它们通常非常简单。但是我的团队在经历了一些非常有害的错误之后,总是使用Func<Task> asyncAction = async () => await FooAsync(hamster.Name);
和async
个关键字。
答案 1 :(得分:4)
这是这两种方法的IL Viewer的输出:
await Runner(() => DoSomethingAsync("no await"));
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task
'<RunTheDemo>b__5_0'() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [42 32 - 42 60]
IL_0000: ldarg.0 // this
IL_0001: ldstr "no await"
IL_0006: call instance class [mscorlib]System.Threading.Tasks.Task TestClass::DoSomethingAsync(string)
IL_000b: ret
} // end of method CompanyManagementController::'<RunTheDemo>b__5_0'
await Runner(async () => await DoSomethingAsync("with await"));
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task
'<RunTheDemo>b__5_1'() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type)
= (
01 00 45 57 65 62 43 61 72 64 2e 43 6f 6e 74 72 // ..TestClass
6f 6c 6c 65 72 73 2e 43 6f 6d 70 61 6e 79 4d 61 // +<<RunTheDemo>
6e 61 67 65 6d 65 6e 74 43 6f 6e 74 72 6f 6c 6c // b__5_1>d..
65 72 2b 3c 3c 52 75 6e 54 68 65 44 65 6d 6f 3e
62 5f 5f 35 5f 31 3e 64 00 00
)
// MetadataClassType(TestClass+<<RunTheDemo>b__5_1>d)
.custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor()
= (01 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 2
.locals init (
[0] class TestClass/'<<RunTheDemo>b__5_1>d' V_0,
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
)
IL_0000: newobj instance void TestClass/'<<RunTheDemo>b__5_1>d'::.ctor()
IL_0005: stloc.0 // V_0
IL_0006: ldloc.0 // V_0
IL_0007: ldarg.0 // this
IL_0008: stfld class TestClass TestClass/'<<RunTheDemo>b__5_1>d'::'<>4__this'
IL_000d: ldloc.0 // V_0
IL_000e: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
IL_0013: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0018: ldloc.0 // V_0
IL_0019: ldc.i4.m1
IL_001a: stfld int32 TestClass/'<<RunTheDemo>b__5_1>d'::'<>1__state'
IL_001f: ldloc.0 // V_0
IL_0020: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0025: stloc.1 // V_1
IL_0026: ldloca.s V_1
IL_0028: ldloca.s V_0
IL_002a: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<class TestClass/'<<RunTheDemo>b__5_1>d'>(!!0/*class TestClass/'<<RunTheDemo>b__5_1>d'*/&)
IL_002f: ldloc.0 // V_0
IL_0030: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0035: call instance class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
IL_003a: ret
} // end of method CompanyManagementController::'<RunTheDemo>b__5_1'
所以第二个使用异步状态机
答案 2 :(得分:3)
是的,它们是相同的,但这是一个相当简单的例子。这两者在功能上是等价的,你只是(可能,取决于编译器)在使用async
时做更多的工作。
一个更好的案例,看看为什么async
lambda是有用的,如果你需要处理一系列异步操作 - 那就是await
的用途,毕竟:
await Runner(async () => await DoSomethingAsync(await httpClient.Get("www.google.com")));