비동기 메서드가 완료되기를 기다리는 방법은 무엇입니까?


138

데이터를 USB HID 클래스 장치로 전송하는 WinForms 응용 프로그램을 작성 중입니다. 내 응용 프로그램을 찾을 수있는 우수한 일반 HID 라이브러리 6.0 사용 여기를 . 간단히 말해서, 장치에 데이터를 써야 할 때 이것이 호출되는 코드입니다.

private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        RequestToGetInputReport();
    }
}

코드가 while 루프에서 빠지면 장치에서 일부 데이터를 읽어야합니다. 그러나 장치가 바로 응답 할 수 없으므로 계속 진행하기 전에이 통화가 돌아올 때까지 기다려야합니다. 현재 존재하므로 RequestToGetInputReport ()는 다음과 같이 선언됩니다.

private async void RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

가치가있는 것에 대한 GetInputReportViaInterruptTransfer () 선언은 다음과 같습니다.

internal async Task<int> GetInputReportViaInterruptTransfer()

불행히도, 나는 .NET 4.5의 새로운 비동기 / 대기 기술의 작동에 익숙하지 않습니다. 나는 await 키워드에 대해 조금 읽었고 RequestToGetInputReport () 내부의 GetInputReportViaInterruptTransfer () 호출이 대기 할 것이라는 인상을 받았지만 RequestToGetInputReport () 호출과 같지 않습니다. while 루프를 거의 즉시 다시 입력하기 때문에 자체적으로 기다리고 있습니까?

아무도 내가보고있는 행동을 분명히 할 수 있습니까?

답변:


131

피하십시오 async void. Task대신 메소드를 반환하십시오 void. 그럼 당신은 할 수 await있습니다.

이처럼 :

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        await RequestToGetInputReport();
    }
}

private async Task RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

1
아주 좋습니다 감사 해요. 나는 비슷한 문제로 머리를 긁고 있었고 차이점은 당신이 말한대로 변경 void하는 Task것이 었습니다.
Jeremy

8
사소한 일이지만 규칙을 따르려면 두 메소드 모두 이름에 Async를 추가해야합니다. 예 : RequestToGetInputReportAsync ()
tymtam

6
발신자가 주 기능인 경우 어떻게해야합니까?
symbiont

14
@symbiont : 그렇다면 사용GetAwaiter().GetResult()
Stephen Cleary

4
@AhmedSalah Task메소드의 실행을 나타내므로 return값이 배치 Task.Result되고 예외가 설정됩니다 Task.Exception. 를 사용 void하면 컴파일러에서 예외를 배치 할 위치가 없으므로 스레드 풀 스레드에서 다시 발생합니다.
Stephen Cleary

229

가장 중요한 것은에 대해 알아야 할 사항 asyncawait그가 await 되지 않습니다 완료 될 때까지 관련 전화 기다립니다. 어떤 await일은 즉시 동 기적으로 작업의 결과를 반환하는 작업이 이미 완료 한 경우 나, 그렇지 않은 경우의 나머지 실행하기 위해 계속 예약 async방법을 다음 호출자에게 컨트롤을 반환 할 수 있습니다. 비동기 작업이 완료되면 예약 완료가 실행됩니다.

질문 제목의 특정 질문에 대한 답변 은 적절한 메소드 를 호출하여 async메소드의 반환 값 ( Task또는 유형이어야 함 )을 차단하는 것입니다 .Task<T>Wait

public static async Task<Foo> GetFooAsync()
{
    // Start asynchronous operation(s) and return associated task.
    ...
}

public static Foo CallGetFooAsyncAndWaitOnResult()
{
    var task = GetFooAsync();
    task.Wait(); // Blocks current thread until GetFooAsync task completes
                 // For pedagogical use only: in general, don't do this!
    var result = task.Result;
    return result;
}

이 코드 스 니펫 CallGetFooAsyncAndWaitOnResult은 비동기 메소드 주위 의 동기 랩퍼 GetFooAsync입니다. 그러나이 패턴은 비동기 작업 중에 전체 스레드 풀 스레드를 차단하므로 대부분 피해야합니다. 이는 API에 노출 된 다양한 비동기 메커니즘을 비효율적으로 사용하여이를 제공하기 위해 많은 노력을 기울입니다.

"await" 의 답변 은 통화완료 될 때까지 기다리지 않습니다 . 이러한 키워드에 대한 자세한 설명이 몇 가지 있습니다.

한편 @Stephen Cleary의 지침은 async void보류입니다. 이유에 대한 다른 좋은 설명은 http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/https://jaylee.org/archive/ 에서 찾을 수 있습니다. 2012 / 07 / 08 / c-sharp-async-tips-and-tricks-part-2-async-void.html


18
나는 await"비동기 대기"로 생각하고 이야기하는 것이 유용하다는 것을 알았습니다. 즉, 메소드는 (필요한 경우) 차단 하지만 스레드 는 차단 하지 않습니다 . 따라서 차단 대기 시간이 아니지만 RequestToSendOutputReport"대기" 에 대해 이야기하는 것이 좋습니다 . RequestToGetInputReport
Stephen Cleary

@Richard Cook-추가 설명을 주셔서 대단히 감사합니다!
bmt22033

10
실제 질문에 더 명확하게 대답하기 때문에 (예 : 비동기 방식에서 스레드 방식으로 차단하는 방법) 허용되는 답변이어야합니다.
csvan

가장 좋은 해결책은 작업이 완료 될 때까지 비동기 대기하는 것입니다. var result = Task.Run (async () => {return await yourMethod ();}). Result;
Ram chittala 2016 년

70

AsynMethod가 작업을 완료 할 때까지 기다리는 가장 좋은 방법은

var result = Task.Run(async() => await yourAsyncMethod()).Result;

15
또는 비동기 "void"의 경우 : Task.Run (async () => {await yourAsyncMethod ();}). Wait ();
Jiří Herník

1
yourAsyncMethod (). Result에 비해 이것의 이점은 무엇입니까?
저스틴 J 스타크

1
.Result 속성에 액세스하는 것은 실제로 작업이 완료 될 때까지 기다리지 않습니다. 사실, 작업이 완료되기 전에 호출되면 예외가 발생한다고 생각합니다. 리차드 쿡이 아래에 언급 한 것처럼 "await"는 실제로 작업이 완료 될 때까지 기다리지 않지만 .Wait () 호출을 사용하면 전체 스레드 풀이 차단된다는 점이 Task.Run () 호출로 래핑되는 이점은 . 이를 통해 별도의 스레드에서 비동기 메소드를 (동기식으로) 실행할 수 있습니다. 약간 혼란 스럽지만 거기에 있습니다.
Lucas Leblanc

내가 필요로했던 결과를 여기에
Gerry

assync () 또는 대기와 같은 빠른 미리 알림 ECMA7 기능은 ECMA7 이전 환경에서는 작동하지 않습니다.
Mbotet

0

다음은 플래그를 사용하는 해결 방법입니다.

//outside your event or method, but inside your class
private bool IsExecuted = false;

private async Task MethodA()
{

//Do Stuff Here

IsExecuted = true;
}

.
.
.

//Inside your event or method

{
await MethodA();

while (!isExecuted) Thread.Sleep(200); // <-------

await MethodB();
}

-1

작업이 완료 될 때까지 대기하기 위해 Wait ()를 넣으십시오.

GetInputReportViaInterruptTransfer().Wait();


현재 스레드를 차단합니다. 따라서 이것은 일반적으로 나쁜 일입니다.
Pure.Krome

-5

다음 스 니펫은 호출자에게 리턴하기 전에 대기중인 메소드가 완료되도록하는 방법을 보여줍니다. 그러나 나는 그것이 좋은 습관이라고 말하지 않을 것입니다. 당신이 다르게 생각한다면 설명과 함께 내 대답을 편집하십시오.

public async Task AnAsyncMethodThatCompletes()
{
    await SomeAsyncMethod();
    DoSomeMoreStuff();
    await Task.Factory.StartNew(() => { }); // <-- This line here, at the end
}

await AnAsyncMethodThatCompletes();
Console.WriteLine("AnAsyncMethodThatCompletes() completed.")

Downvoters, 답변에서 요청한 것처럼 설명해야합니까? 이것이 내가 아는 한 잘 작동하기 때문에 ...
Jerther

3
문제는 당신이 await+ 를 할 수있는 유일한 방법 Console.WriteLine은 a가되는 것 Task입니다.이 둘 사이의 제어권을 포기합니다. 따라서 귀하의 '솔루션'은 궁극적으로 Task<T>문제를 해결하지 않는를 산출합니다 . A는 이렇게 Task.Wait의지하는 것은 실제로 (교착 가능성 등으로) 처리를 중지합니다. 다시 말해서 await실제로 기다리지 않고 단순히 두 개의 비동기 적으로 실행 가능한 부분을 하나의 Task(누군가 보거나 기다릴 수 있는) 단일 부분으로 결합합니다.
Ruben Bartelink

-5

실제로 이것은 IAsyncAction을 반환하는 함수에 더 유용하다는 것을 알았습니다.

            var task = asyncFunction();
            while (task.Status == AsyncStatus.Completed) ;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.