“await Task.Run (); 반환;" 그리고“Return Task.Run ()”?
다음 두 코드 사이에 개념적 차이가 있습니까?
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
/의 await
버전은 죽은 잠금이 아닌 기본 동기화 상황에 더 많은 경향이있다. 예를 들어 다음은 WinForms 또는 WPF 응용 프로그램에서 교착 상태가됩니다.
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
방법, 반환에 저장됩니다
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
) 현재 스레드의 동기화 컨텍스트에서 즉시 다시 throw됩니다 ( 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
여전히 비동기 적으로 실행될 수 있습니다.
차이점은 무엇입니까
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()); }
There is major conceptual difference between these two. The first one is asynchronous, the second one is not. Read Async Performance: Understanding the Costs of Async and Await to get a little more about internals of
async
/await
.They do generate different code.
.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
and
.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
The two examples do differ. When a method is marked with the async
keyword, the compiler generates a state-machine behind the scenes. This is what is responsible for resuming continuations once an awaitable has been awaited.
In contrast, when a method is not marked with async
you are losing the ability to await
awaitables. (That is, within the method itself; the method can still be awaited by its caller.) However, by avoiding the async
keyword, you are no longer generating the state-machine, which can add a fair bit of overhead (lifting locals to fields of the state-machine, additional objects to the GC).
In examples like this, if you are able to avoid async-await
and return an awaitable directly, it should be done to improve the efficiency of the method.
See this question and this answer which are very similar to your question and this answer.
'Nice programing' 카테고리의 다른 글
실패한 JUnit 테스트를 즉시 다시 실행하는 방법은 무엇입니까? (0) | 2020.10.16 |
---|---|
회전 및 번역 (0) | 2020.10.16 |
크기가 다른 경우 분류를 위해 이미지를 훈련하는 방법은 무엇입니까? (0) | 2020.10.16 |
datetime의 MySQL 스트립 시간 구성 요소 (0) | 2020.10.16 |
Apache의 SetEnv와 유사한 Nginx 변수? (0) | 2020.10.15 |