다음 두 코드 사이에 개념적 차이가 있습니까?
async Task TestAsync()
{
await Task.Run(() => DoSomeWork());
}
과
Task TestAsync()
{
return Task.Run(() => DoSomeWork());
}
생성 된 코드도 다른가요?
편집 : 와 혼동을 피하기 위해 Task.Run유사한 경우 :
async Task TestAsync()
{
await Task.Delay(1000);
}
과
Task TestAsync()
{
return Task.Delay(1000);
}
최신 업데이트 : 수락 된 답변 외에도 LocalCallContext처리 방법에도 차이가 있습니다 . CallContext.LogicalGetData는 비동기가없는 경우에도 복원됩니다. 왜?
답변
한 가지 주요 차이점은 예외 전파입니다. 내부 던져진 예외 상황, async Task방법, 반환에 저장됩니다 Task객체와 작업을 통해 관찰 때까지 휴면 남아 await task, task.Wait(), task.Result또는 task.GetAwaiter().GetResult(). 메서드 의 동기 부분 에서 throw 되더라도 이러한 방식으로 전파 async됩니다.
다음 코드를 고려 곳 OneTestAsync과 AnotherTestAsync상당히 다르게 동작 :
static async Task OneTestAsync(int n)
{
await Task.Delay(n);
}
static Task AnotherTestAsync(int n)
{
return Task.Delay(n);
}
// call DoTestAsync with either OneTestAsync or AnotherTestAsync as whatTest
static void DoTestAsync(Func<int, Task> whatTest, int n)
{
Task task = null;
try
{
// start the task
task = whatTest(n);
// do some other stuff,
// while the task is pending
Console.Write("Press enter to continue");
Console.ReadLine();
task.Wait();
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
}
을 호출 DoTestAsync(OneTestAsync, -2)하면 다음과 같은 출력이 생성됩니다.
계속하려면 Enter 키를 누르세요. 오류 : 하나 이상의 오류가 발생했습니다. await Task.Delay 오류 : 두 번째
나는 Enter그것을보기 위해 눌러야 했다.
이제를 호출하면 DoTestAsync(AnotherTestAsync, -2)내부의 코드 워크 플로 DoTestAsync가 상당히 다르며 출력도 마찬가지입니다. 이번에는 다음을 누르라는 요청을받지 않았습니다 Enter.
오류 : 값은 -1 (무한 제한 시간을 의미), 0 또는 양의 정수 여야합니다. 매개 변수 이름 : millisecondsDelayError : 1st
두 경우 모두 Task.Delay(-2)매개 변수의 유효성을 검사하면서 처음에 발생합니다. 이것은 구성 시나리오 일 수 있지만 이론적으로 Task.Delay(1000)는 예를 들어 기본 시스템 타이머 API가 실패하는 경우에도 발생할 수 있습니다.
참고로, 오류 전파 논리는 async void메서드 ( 메소드 와 반대)에 대해 아직 다릅니다 async Task. async void메서드 내에서 발생한 예외 SynchronizationContext.Post는 현재 스레드가 하나 ( SynchronizationContext.Current != null). 그렇지 않으면를 통해 다시 throw됩니다 ThreadPool.QueueUserWorkItem. 호출자는 동일한 스택 프레임에서이 예외를 처리 할 기회가 없습니다.
여기 와 여기 에 TPL 예외 처리 동작에 대한 자세한 내용을 게시했습니다 .
Q : async비동기 Task기반이 아닌 메서드에 대한 메서드 의 예외 전파 동작을 모방 하여 후자가 동일한 스택 프레임에서 발생하지 않도록 할 수 있습니까?
A : 정말로 필요한 경우, 예, 이에 대한 트릭이 있습니다.
// async
async Task<int> MethodAsync(int arg)
{
if (arg < 0)
throw new ArgumentException("arg");
// ...
return 42 + arg;
}
// non-async
Task<int> MethodAsync(int arg)
{
var task = new Task<int>(() =>
{
if (arg < 0)
throw new ArgumentException("arg");
// ...
return 42 + arg;
});
task.RunSynchronously(TaskScheduler.Default);
return task;
}
그러나 특정 조건 (예 : 스택에 너무 깊숙한 경우)에서는 RunSynchronously여전히 비동기 적으로 실행될 수 있습니다.
또 다른 주목할만한 차이가 있다는 것입니다 /의 버전은 죽은 잠금이 아닌 기본 동기화 상황에 더 많은 경향이있다 . 예를 들어 다음은 WinForms 또는 WPF 응용 프로그램에서 교착 상태가됩니다.asyncawait
static async Task TestAsync()
{
await Task.Delay(1000);
}
void Form_Load(object sender, EventArgs e)
{
TestAsync().Wait(); // dead-lock here
}
비동기 버전으로 변경하면 교착 상태가되지 않습니다.
Task TestAsync()
{
return Task.Delay(1000);
}
교착 상태의 특성은 그의 블로그 에서 Stephen Cleary에 의해 잘 설명됩니다 .
답변
차이점은 무엇입니까
async Task TestAsync() { await Task.Delay(1000); }과
Task TestAsync() { return Task.Delay(1000); }?
이 질문에 혼란 스럽습니다. 다른 질문으로 귀하의 질문에 답하여 명확히하겠습니다. 차이점은 무엇입니까?
Func<int> MakeFunction()
{
Func<int> f = ()=>1;
return ()=>f();
}
과
Func<int> MakeFunction()
{
return ()=>1;
}
?
내 두 가지의 차이점이 무엇이든간에 동일한 차이는 두 가지입니다.
답변
-
첫 번째 방법은 컴파일도하지 않습니다.
‘
Program.TestAsync()‘는 ‘ ‘를 반환하는 비동기 메서드이므로 반환Task키워드 뒤에 개체식이 올 수 없습니다. ‘Task<T>‘ 을 (를) 반환하려고 했습니까 ?그건 그래야만 해
async Task TestAsync() { await Task.Run(() => DoSomeWork()); } -
이 둘 사이에는 큰 개념적 차이가 있습니다. 첫 번째는 비동기식이고 두 번째는 비동기식입니다. Async Performance : Understanding the Costs of Async and Await to get more about internals of
async/await. -
그들은 다른 코드를 생성합니다.
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task TestAsync () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 25 53 4f 54 65 73 74 50 72 6f 6a 65 63 74 2e 50 72 6f 67 72 61 6d 2b 3c 54 65 73 74 41 73 79 6e 63 3e 64 5f 5f 31 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x216c // Code size 62 (0x3e) .maxstack 2 .locals init ( [0] valuetype SOTestProject.Program/'<TestAsync>d__1', [1] class [mscorlib]System.Threading.Tasks.Task, [2] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ) IL_0000: ldloca.s 0 IL_0002: ldarg.0 IL_0003: stfld class SOTestProject.Program SOTestProject.Program/'<TestAsync>d__1'::'<>4__this' IL_0008: ldloca.s 0 IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create() IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0014: ldloca.s 0 IL_0016: ldc.i4.m1 IL_0017: stfld int32 SOTestProject.Program/'<TestAsync>d__1'::'<>1__state' IL_001c: ldloca.s 0 IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0023: stloc.2 IL_0024: ldloca.s 2 IL_0026: ldloca.s 0 IL_0028: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<valuetype SOTestProject.Program/'<TestAsync>d__1'>(!!0&) IL_002d: ldloca.s 0 IL_002f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder' IL_0034: call instance class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task() IL_0039: stloc.1 IL_003a: br.s IL_003c IL_003c: ldloc.1 IL_003d: ret } // end of method Program::TestAsync과
.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task TestAsync2 () cil managed { // Method begins at RVA 0x21d8 // Code size 23 (0x17) .maxstack 2 .locals init ( [0] class [mscorlib]System.Threading.Tasks.Task CS$1$0000 ) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldftn instance class [mscorlib]System.Threading.Tasks.Task SOTestProject.Program::'<TestAsync2>b__4'() IL_0008: newobj instance void class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>::.ctor(object, native int) IL_000d: call class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Run(class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method Program::TestAsync2
답변
두 가지 예 는 다릅니다. 메서드가 async키워드 로 표시 되면 컴파일러는 백그라운드에서 상태 머신을 생성합니다. 이것은 awaitable이 기다린 후에 연속을 재개하는 책임입니다.
대조적으로, 메소드가 표시 되지 않으면 대기 가능 async기능을 await잃게됩니다. (즉, 메서드 자체 내에서 메서드는 호출자가 계속 기다릴 수 있습니다.) 그러나 async키워드 를 피하면 더 이상 상태 머신을 생성하지 않아 상당한 오버 헤드를 추가 할 수 있습니다 (로컬을 필드로 끌어 올림). 상태 머신, GC에 대한 추가 개체).
이와 같은 예 async-await에서 awaitable을 직접 피하고 반환 할 수 있다면 메서드의 효율성을 높이기 위해 수행해야합니다.
답변
