Nice programing

“await Task.Run ();

nicepro 2020. 10. 16. 08:02
반응형

“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됩니다.

다음 코드를 고려 곳 OneTestAsyncAnotherTestAsync상당히 다르게 동작 :

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;
}

?

내 두 가지의 차이점이 무엇이든간에 동일한 차이는 두 가지입니다.


  1. 첫 번째 방법은 컴파일도하지 않습니다.

    ' Program.TestAsync()'는 ' '를 반환하는 비동기 메서드이므로 반환 Task키워드 뒤에 개체식이 올 수 없습니다. ' Task<T>' 을 (를) 반환하려고 했습니까 ?

    그건 그래야만 해

    async Task TestAsync()
    {
        await Task.Run(() => DoSomeWork());
    }
    
  2. 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.

  3. 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.

참고URL : https://stackoverflow.com/questions/21033150/any-difference-between-await-task-run-return-and-return-task-run

반응형