'async'및 'await'사용 방법 및시기


1065

내가 이해 async하고await 수행 하는 주요 작업 중 하나는 코드를 쉽게 읽고 읽을 수 있도록하는 것입니다. 그러나 긴 스레드를 수행하기 위해 백그라운드 스레드를 생성하는 것과 동일합니까?

나는 현재 가장 기본적인 예를 시도하고 있습니다. 의견을 인라인으로 추가했습니다. 나를 위해 그것을 명확히 할 수 있습니까?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
또한 예제에서 위 코드를 컴파일 할 때 경고가 표시됩니다. 경고에주의하십시오 . 이 코드는 이해가되지 않는다고 알려줍니다.
Eric Lippert

답변:


759

사용하는 경우 asyncawait컴파일러는 백그라운드에서 상태 머신을 생성합니다.

다음은 진행중인 일부 세부 정보를 설명 할 수있는 예입니다.

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

자, 여기서 일어나는 일은 :

  1. Task<int> longRunningTask = LongRunningOperationAsync(); 실행을 시작합니다 LongRunningOperation

  2. 독립적 인 작업은 Main Thread (Thread ID = 1) await longRunningTask에 도달 했다고 가정합니다 .

    이제 longRunningTask완료되지 않고 여전히 실행 중이 MyMethodAsync()면 호출 메소드로 돌아가서 기본 스레드가 차단되지 않습니다. 작업 longRunningTask이 완료 되면 ThreadPool의 스레드 (모든 스레드 일 수 있음)가 MyMethodAsync()이전 컨텍스트로 돌아가서 실행을 계속합니다 (이 경우 콘솔에 결과 인쇄).

두 번째 경우 longRunningTask는 이미 실행을 마치고 결과를 사용할 수 있다는 것입니다. 에 도달하면 await longRunningTask결과가 이미 있으므로 코드는 동일한 스레드에서 계속 실행됩니다. (이 경우 콘솔에 결과 인쇄). 물론 이것은 위의 예에서는 해당되지 않습니다 Task.Delay(1000).


65
"Task.Delay (1000);"과 함께 "대기"가 발생하는 이유는 무엇입니까? LongRunningOperation 비동기 메소드에서?
Benison Sam

3
@codea Eric Lippert의 기사에 대한 기사 에서이 주제에 대한 소개 기사연결하여 DoEvents 전략을 async-await와 구체적으로 비교했습니다.
Camilo Martinez

13
@ BenisonSam 스레드는 조금 낡았지만 같은 질문이 있었고 답을 찾고있었습니다. "await"의 이유는 "await"를 생략하면 LongRunningOperationAsync ()가 즉시 리턴되기 때문입니다. 실제로 await를 제거하면 컴파일러에서 경고를 표시합니다. Stephen Cleary의 블로그 게시물 blog.stephencleary.com/2011/09/…뛰어난 디자인 토론을 제공합니다.
shelbypereira

70
모든 비동기 메소드가 내부에 대기를 필요로하고 비동기가있는 메소드에서만 대기를 수행 할 수 있다면 언제 중지합니까?
Bruno Santos

108
이 대답은 분명히 틀렸다. 이러한 많은 투표는 많은 사용자에게 잘못된 이해를 야기 할 것입니다. MS 문서는 분명히 async를 사용할 때 다른 스레드가 사용되지 않는다고 말합니다. msdn.microsoft.com/en-us/library/mt674882.aspx 누군가 대답을 수정하십시오. 이로 인해 나는 하루 종일 낭비했습니다.
Krishna Deepak

171

내가 이해하고 비동기 및 대기하는 주요 작업 중 하나는 코드를 쉽게 읽고 읽을 수 있도록 만드는 것입니다.

그들은 할 것 비동기 네, 쓰기에 쉽게 코드를 읽고.

장시간 지속되는 논리를 수행하기 위해 백그라운드 스레드를 생성하는 것과 같은 것입니까?

전혀.

//이 방법이 왜 '비동기'로 표시되어야하는지 이해가되지 않습니다.

async키워드는 수 await키워드를. 따라서 사용하는 방법 await은 모두 표시해야합니다 async.

// DoSomethingAsync () 메소드에서 5 초간 휴면 한 후이 라인에 도달합니다. 즉시 도달해서는 안됩니까?

async기본적으로 메소드는 다른 스레드에서 실행되지 않기 때문 입니다.

// 백그라운드 스레드에서 실행됩니까?

아니.


async/ await소개가 도움 이 될 수 있습니다. 공식 MSDN의 문서는 또한 (특히 비정상적으로 좋은 TAP의 섹션), 그리고 async팀은 훌륭한 넣어 FAQ를 .


6
따라서 백그라운드 스레드에서 실행되지 않지만 차단되지는 않습니다. 스레드로 저글링하는 대신 콜백을 사용하는 비동기 API로 인해 가능합니다. (I / O, socket, ..) 작업을 시작하고 작업을 다시 시작합니다. 작업이 완료되면 OS가 콜백을 호출합니다. 이것이 Node.js 또는 Python Twisted 프레임 워크의 기능이며 이에 대한 좋은 설명도 있습니다.
Roman Plášil

3
"async 키워드는 await 키워드를 활성화합니다. await를 사용하는 모든 메소드는 async로 표시되어야합니다."-왜 그렇습니까? 이 답변은 왜 메소드가 비동기로 표시되어야하는지 이해하는 데 도움이되지 않습니다. 컴파일러는 await 키워드를 내부에서 검색하여 메소드가 비동기식이라고 유추 할 수 없습니까?
Stanislav

9
@Stanislav : 그 질문을 다루는 블로그 항목 이 있습니다.
Stephen Cleary

3
제안 된 설명 : 아니요 async. 기본적으로 메소드는 다른 스레드에서 실행되지 않기 때문 입니다. 예를 들어, Sleep()within 내의 호출 DoSomethingAsync()은 현재 스레드를 차단하여 완료 button1_Click()될 때까지 실행이 계속되지 않도록 DoSomethingAsync()합니다. Thread.Sleep()실행 스레드 를 차단 하는 동안Task.Delay() does not.
DavidRR

166

설명

다음은 async/ await에 대한 간단한 예입니다 . 이 이상으로 고려해야 할 더 많은 세부 사항이 있습니다.

참고 : Task.Delay(1000)1 초 동안 작업하는 것을 시뮬레이션합니다. 외부 리소스의 응답을 기다리는 것으로 생각하는 것이 가장 좋습니다. 코드가 응답을 기다리는 중이므로 시스템은 실행중인 작업을 한쪽으로 설정하고 완료되면 다시 돌아올 수 있습니다. 그 동안 스레드에서 다른 작업을 수행 할 수 있습니다.

아래 예제에서 첫 번째 블록 은 정확히 그렇게합니다. 모든 작업을 즉시 시작하고 ( Task.Delay줄) 측면으로 설정합니다. await a다음 줄로 넘어 가기 전에 1 초 지연이 완료 될 때까지 줄 에서 코드가 일시 중지됩니다 . 이후 b, c, d, 그리고 e모두 거의 동일한 시간에 실행 시작 a(때문에 AWAIT 부족), 그들은이 경우 거의 같은 시간에 완료해야한다.

아래 예에서 두 번째 블록 은 작업 await을 시작하고 후속 작업을 시작하기 전에 작업 이 완료되기를 기다리는 것입니다 . 이 과정을 반복 할 때마다 1 초가 걸립니다. 는 await프로그램을 일시 중지하고 계속하기 전에 결과를 기다리고있다. 이것이 첫 번째 블록과 두 번째 블록의 주요 차이점입니다.

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

산출:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

SynchronizationContext에 관한 추가 정보

참고 :이 곳은 약간 안개가 자욱한 곳이므로 내가 잘못하면 정정하십시오. 답을 업데이트하겠습니다. 이것이 어떻게 작동하는지에 대한 기본 지식을 갖는 것이 중요하지만 사용하지 않는 한 전문가가 ConfigureAwait(false)아니더라도 얻을 수는 있지만 최적화의 기회를 잃을 수도 있습니다.

이것의 한 가지 측면이있어 async/ await개념을 이해하기가 다소 까다로워집니다. 이것이이 예에서, 이것은 모두 동일한 스레드에서 발생한다는 것입니다 (또는 적어도 해당 스레드와 관련하여 동일한 스레드로 보이는 것 SynchronizationContext). 기본적 await으로 실행중인 원래 스레드의 동기화 컨텍스트를 복원합니다. 예를 들어 ASP.NET HttpContext에는 요청이 들어올 때 스레드에 연결된 것이 있습니다.이 컨텍스트에는 언어, IP 주소, 헤더 등과 같은 원래 Request 객체와 같은 원래 Http 요청에 특정한 항목이 포함됩니다. 무언가를 처리하면서 스레드를 반쯤 전환하면 잠재적으로 다른 객체에서이 객체에서 정보를 가져 오려고 할 수 있습니다.HttpContext비참 할 수 있습니다. 어떤 상황에서도 컨텍스트를 사용하지 않을 경우 "무관심"하도록 선택할 수 있습니다. 이를 통해 기본적으로 코드를 사용하지 않고 별도의 스레드에서 코드를 실행할 수 있습니다.

이것을 어떻게 달성합니까? 기본적으로 await a;코드는 실제로 컨텍스트를 캡처하고 복원하려고한다고 가정합니다.

await a; //Same as the line below
await a.ConfigureAwait(true);

메인 코드가 원래 컨텍스트없이 새 스레드에서 계속되도록하려면 컨텍스트 대신 복원 할 필요가 없다는 것을 알기 때문에 true 대신 false를 사용하면됩니다.

await a.ConfigureAwait(false);

프로그램이 일시 중지 된 후에는 다른 컨텍스트를 가진 완전히 다른 스레드에서 계속 될 수 있습니다. 여기에서 성능 향상이 시작됩니다. 시작한 원래 컨텍스트를 복원하지 않고도 사용 가능한 스레드에서 계속 진행할 수 있습니다.

이 물건이 혼란 스럽습니까? 그럼 당연하지! 알아낼 수 있습니까? 아마! 당신은 개념의 이해가 있으면, 다음 더 기술적 인 이해를 가진 사람 위해 개발되는 경향이 스티븐 클리어 리의 설명에 이동 async/ await이미.


이 모든 작업이 int를 반환하고 두 번째 작업 (또는 일부 계산)에서 첫 번째 작업의 결과를 사용하는 경우 잘못된 것입니까?
veerendra gupta

3
@veerendragupta 예. 이 경우 비동기식이 아니기 때문에 의도적으로 비동기식으로 실행하지 않도록 선택합니다. 여기에 들어 가지 않는 구성 컨텍스트와 관련하여 알아야 할 몇 가지 사항도 있습니다.
Joe Phillips

그래서 await MethodCall()절대 낭비인가? await/ async?를 떨어 뜨릴 수도 있습니다 .
Vitani

2
@Jocie 그렇지 않습니다. 을 호출 await하면 스레드를 유지하는 대신 스레드를 다시 풀로 해제한다고 생각합니다. 이 작업의 반환을 기다리는 동안 다른 곳에서 사용할 수 있습니다
Joe Phillips

2
@JoePhillips 방금 말한 것은 비동기 / 대기의 본질이라고 생각합니다. 호출 스레드가 해제되어 시스템의 다른 프로세스에서 사용할 수 있습니다. 대기 호출이 완료되면 새 스레드가 호출자의 원래 시작을 재개하는 데 사용됩니다. 호출자는 여전히 대기 중이지만 그 동안 스레드가 해제된다는 이점이 있습니다. 이것이 비동기 / 대기의 이점입니까?
밥 혼

147

다른 답변들보다 더 기다려보십시오 (C # Reference)

더 구체적으로 포함 된 예에서는 상황을 조금 설명합니다.

다음 Windows Forms 예제에서는 비동기 메서드 WaitAsynchronouslyAsync에서 await를 사용하는 방법을 보여줍니다. 해당 메소드의 동작과 WaitSynchronously의 동작을 대조하십시오. await 연산자가 작업에 적용되지 않으면 WaitSynchronously는 정의에서 비동기 수정자를 사용하고 본문에서 Thread.Sleep을 호출하더라도 동 기적으로 실행됩니다.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
답변 해주셔서 감사합니다. 그러나 WaitAsynchronouslyAsync ()는 별도의 스레드에서 실행됩니까?
Dan Dinu

32
에서 기다리십시오. 표현식은 실행중인 스레드를 차단하지 않습니다. 대신, 컴파일러는 대기중인 작업의 연속으로 나머지 비동기 메소드에 서명합니다. 그런 다음 컨트롤은 async 메서드의 호출자로 돌아갑니다. 작업이 완료되면 작업이 계속되고 비동기 메서드 실행이 중단 된 지점에서 다시 시작됩니다.
Adriaan Stander

13
이 MSDN 기사 에 따르면 , "async 및 await 키워드는 추가 스레드를 생성하지 않습니다 ... 비동기 메소드는 자체 스레드에서 실행되지 않습니다". 필자는 이해를 돕기 위해 키워드를 기다릴 때 프레임 워크가 건너 뛰고 (호출자에게 다시) 긴 작업이 완료되기를 기다리는 동안 가능한 모든 독립적 코드를 실행할 수 있습니다. 즉, 모든 독립 코드가 실행되면 긴 작업이 반환되지 않으면 차단됩니다. 그래도 지금 이것을 배우고 있습니다.
Vimes

9
@astander 맞지 않다. 다른 스레드 에서는 실행 되지 않습니다 . 단지 타이머가 사용될 때 호출이 계속되도록 (그 외 메소드의) 스케줄을 설정합니다 Task.Delay.
MgSam

1
이 답변은 수면으로 인해 잘못되었습니다. Await Task.Delay (1000);으로 허용 된 답변을 참조하십시오. 올바른 동작이 있습니다.
Jared Updike 2013

62

간단한 콘솔 프로그램에서 위의 설명을 보여줍니다.

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

그리고 출력은 다음과 같습니다

Starting Long Running method...
Press any key to exit...
End Long Running method...

그러므로,

  1. 메인은을 통해 장기 실행 방법을 시작합니다 TestAsyncAwaitMethods. 현재 스레드를 중단하지 않고 즉시 반환되며 '종료하려면 아무 키나 누르십시오'라는 메시지가 즉시 표시됩니다.
  2. 이 모든 동안 LongRunningMethod백그라운드에서 실행 중입니다. 완료되면 Threadpool의 다른 스레드가이 컨텍스트를 선택하고 최종 메시지를 표시합니다.

따라서 스레드가 차단되지 않습니다.


"출력하려면 아무 키나 누르십시오 ..."가 출력의 어느 부분에 표시됩니까?
StudioX

1
그리고 (return 1)의 사용법은 무엇입니까? 그게 필요 할까?
StudioX

1
@StudioX 내가 그것을 반환 형식 정수가 있어야합니다 생각
쿠바 마

나는 그 return 1부분에 대한 추가 설명이 필요 하다고 생각합니다 . await키워드를 사용하면 기본 유형을 Task<T>직접 반환 할 수 있으므로 종료 코드를 대기 / 비동기 세계에 쉽게 적용 할 수 있습니다. 그러나 반환 유형을 지정하지 않고 를 리턴 할 수 있으므로 값을 리턴 할 필요가 없습니다 . Task이는 동기 void메소드 와 같습니다 . C #은 async void메소드를 허용 하지만 이벤트 핸들러를 다루지 않는 한 피하는 것이 좋습니다.
Christiano Kiss

41

나는 당신이 나쁜 예를 골랐다 고 생각합니다. System.Threading.Thread.Sleep

의 포인트 async작업은 이러한 일을 같이 메인 스레드 잠금없이 백그라운드에서 실행하도록하는 것입니다DownloadFileAsync

System.Threading.Thread.Sleep "완료"된 것이 아니라 잠들기 때문에 5 초 후에 다음 줄에 도달합니다 ...

이 기사를 읽으면 http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx에 대한 훌륭한 설명 asyncawait개념 이라고 생각합니다 .


3
수면이 나쁜 예이지만 다운로드가 좋은 예입니다. Thread.Sleep을 볼 때 FooBar와 같은 것입니다. 시간이 걸리는 작업이 있다는 것을 알고 있습니다. 나는 그의 질문이 적절하다고 생각합니다
Abdurrahim

1
@Abdurrahim Thread.Sleep은 스레드를 차단하지만 (스레드는 다른 작업을 수행 할 수 있지만 유휴 상태로 유지됨 ) 비동기 메소드는 그렇지 않습니다. 의 경우 DownloadFileAsync스레드는 원격 서버에서 응답이 올 때까지 다른 작업을 수행 할 수 있습니다. 비동기 메서드에서 "시간이 걸리는 일부 작업"에 대한 더 나은 자리 표시 Task.Delay자는 실제로 비동기이기 때문에입니다.
Gabriel Luci

@GabrielLuci 내 이의 제기는 지연 대 수면에 관한 것이 아닙니다. 당신의 대답은 밀짚 꾼 대답과 비슷합니다. 이 질문에 대한 의견으로 내가 반대 할 수없는 의견을 제시하면 대답으로 더 밀짚 맨 대답처럼 냄새가납니다. 나는 그들이해야 할 모든 통화가 통화를 차단할 것이므로 비동기식을 사용하는 것이 여전히 좋다고 생각한다. 모든 목적을 무효화하지는 않을 것입니다 ... 모든 좌파조차도 유효한 경우로 간주되는 구문 설탕 일 것입니다.
Abdurrahim

1
이것은 내 대답이 아니었다. 그러나 요점을 해결하려면 방법의 목적에 달려 있습니다. 방금 호출 방법을 원한다면 성공했습니다. 그러나이 경우 그는 비동기 적으로 실행되는 메소드를 만들려고했습니다. 그는 async키워드를 사용하여 그렇게했습니다 . 그러나 그의 방법은 여전히 ​​동기식으로 실행 되었으며이 답변은 실제로 비동기 코드를 실행하지 않았기 때문에 그 이유를 완벽하게 설명했습니다. 불완전 async해질 때까지 표시된 메소드는 여전히 동기식으로 실행 됩니다. 이 없으면 메소드가 동기식으로 실행되고 컴파일러에서이를 경고합니다. awaitTaskawait
Gabriel Luci

23

다음은 팔로우하는 사람들에게 명확하게 알려주는 빠른 콘솔 프로그램입니다. 이 TaskToDo방법은 비 동기화하려는 장기 실행 방법입니다. 비동기 실행은 TestAsync메소드에 의해 수행됩니다 . 테스트 루프 방법은 TaskToDo작업을 통해 실행되고 비동기 적으로 실행됩니다. 결과는 실행에서 동일한 순서로 완료되지 않기 때문에 결과에서 확인할 수 있습니다. 완료되면 콘솔 UI 스레드에보고합니다. 단순하지만 간단한 예제는 더 많은 관련 예제보다 패턴의 핵심을 더 잘 이끌어 낸다고 생각합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

20

가장 빠른 학습을 위해 ..

  • 메소드 실행 플로우 이해 (다이어그램 사용) : 3 분

  • 질문 내성 (학습) : 1 분

  • 구문 설탕을 빠르게 습득하십시오 : 5 분

  • 개발자의 혼란을 공유하십시오 : 5 분

  • 문제점 : 실제 코드의 실제 구현을 비동기 코드로 빠르게 변경 : 2 분

  • 다음에 어디?

메소드 실행 플로우 이해 (다이어그램 사용) : 3 분

이 이미지에서 # 6에 집중하십시오 (아무것도 없습니다) 여기에 이미지 설명을 입력하십시오

# 6 단계 : 실행이 부족하여 실행이 중지되었습니다. 계속하려면 getStringTask (종류의 함수)의 결과가 필요합니다. 따라서await 연산자를 진행 상황을 일시 중단하고 호출자에게 제어권을 부여합니다 (이 방법의). getStringTask에 대한 실제 호출은 2 번 초에 이루어졌습니다. # 2에서 문자열 결과를 반환하기로 약속했습니다. 그러나 결과는 언제 반환됩니까? 우리가 (# 1 : AccessTheWebAsync) 다시 전화를해야합니까? 결과를 얻는 사람, # 2 (호출 진술) 또는 # 6 (발언 대기)

AccessTheWebAsync ()의 외부 호출자도 지금 기다리고 있습니다. 따라서 호출자는 AccessTheWebAsync를 기다리고 있으며 AccessTheWebAsync는 현재 GetStringAsync를 기다리고 있습니다. 흥미로운 점은 AccessTheWebAsync가 대기 (# 4) 전에 대기에서 시간을 절약하기 위해 약간의 작업을 한 것입니다. 외부 발신자 (및 체인의 모든 발신자)에게도 멀티 태스킹에 대한 동일한 자유가 제공되며 이는 이 '비동기'기능의 가장 큰 장점입니다!동기 적이거나 정상적이라고 생각하지만 그렇지 않습니다.

메소드가 이미 리턴되었으므로 (# 2) 다시 리턴 할 수 없습니다 (두 번째는 아님). 그러면 발신자는 어떻게 알 수 있습니까? 작업 에 관한 모든 것입니다 ! 작업이 통과되었습니다. 작업이 기다렸습니다 (값이 아닌 방법이 아님). 작업에서 값이 설정됩니다. 작업 상태가 완료로 설정됩니다. 발신자는 작업 (# 6) 만 모니터링합니다. 6 #은 어디서 / 누가 결과를 얻는 지에 대한 답입니다. 자세한 내용은 여기를 참조하십시오 .

학습을위한 질문 내성 : 1 분

질문을 약간 조정 해 봅시다.

언제 어떻게 사용 하고 ? asyncawait Tasks

학습 Task은 다른 두 가지를 자동으로 다루기 때문에 (질문에 대한 답변)

구문 설탕을 빠르게 습득하십시오 : 5 분

  • 변환 전 (원본 방법)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • 위의 메서드를 호출하는 작업 기반 메서드

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

우리는 대기 또는 비동기 언급 했습니까? 아니요. 위의 방법을 호출하면 모니터링 할 수있는 작업이 나타납니다. 작업이 무엇을 반환하는지 이미 알고 있습니다. 정수.

  • 작업 호출은 약간 까다롭기 때문에 키워드가 나타나기 시작합니다. MethodTask ()를 호출하자

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

위의 이미지와 동일한 코드가 아래 이미지에 추가되었습니다. 여기에 이미지 설명을 입력하십시오

  1. 우리는 작업을 끝내기를 기다리고 있습니다. 따라서await
  2. await를 사용하기 때문에 반드시 사용해야합니다 async(필수 구문).
  3. Async접두사로 사용하는 MethodAsync (코딩 표준)

await이해하기 쉽지만 나머지 두 개 ( async, Async)는 :)가 아닐 수 있습니다. 그래도 컴파일러에게는 훨씬 더 합리적이어야 합니다.

두 부분이 있습니다.

  1. '작업'만들기
  2. 작업을 호출하는 구문 설탕 만들기 ( await+async)

우리는 AccessTheWebAsync ()에 대한 외부 호출자를 가지고 있었고 그 호출자는 절약되지 않았습니다. 즉, 같은 await+async것도 필요합니다 . 그리고 체인은 계속됩니다. 그러나 항상 Task한쪽 끝에 있을 것 입니다.

괜찮습니다.하지만 한 개발자가 # 1 (작업)이 누락 된 것을보고 놀랐습니다 ...

개발자의 혼란을 공유하십시오 : 5 분

개발자가 구현하지 않은 실수를 Task했지만 여전히 작동합니다! 여기에 제공된 질문과 허용 된 답변 만 이해 하십시오 . 당신이 읽고 완전히 이해하기를 바랍니다. 요약하면 '작업'을 보거나 구현하지 못할 수도 있지만 부모 클래스 어딘가에 구현되어 있습니다. 마찬가지로이 예제에서 이미 빌드 된 호출은 ( ) ourself로 MethodAsync()해당 메소드를 구현하는 것보다 훨씬 쉽습니다 . 대부분의 개발자 는 코드를 비동기 코드로 변환하는 동안 어려움을 겪고 있습니다.TaskMethodTask()Tasks

팁 : 기존 비동기 구현 ( MethodAsync또는 같은 ToListAsync)을 찾아서 어려움을 아웃소싱하십시오. 따라서 Async 만 처리하면됩니다 (일반 코드와 쉽고 유사 함).

문제 : 실제 코드의 실제 구현을 비동기 작업으로 빠르게 변경 : 2 분

아래의 데이터 레이어에 표시된 코드 줄이 깨지기 시작했습니다 (많은 곳). 코드 일부를 .Net framework 4.2. *에서 .Net 코어로 업데이트했기 때문입니다. 애플리케이션 전체에서 1 시간 안에이 문제를 해결해야했습니다!

var myContract = query.Where(c => c.ContractID == _contractID).First();

쉬워요!

  1. QueryableExtensions가 있기 때문에 EntityFramework nuget 패키지를 설치했습니다. 즉, 그것은 비동기 구현 (태스크)을 수행하므로 단순 Async하고 await코드로 살아남을 수 있습니다.
  2. 네임 스페이스 = Microsoft.EntityFrameworkCore

호출 코드 줄이 이렇게 변경되었습니다.

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. 메소드 서명이

    Contract GetContract(int contractnumber)

    async Task<Contract> GetContractAsync(int contractnumber)

  2. 호출하는 방법도 영향을 받았습니다 : GetContractAsync(123456);로 불렀다GetContractAsync(123456).Result;

  3. 우리는 그것을 30 분 만에 모든 곳에서 바 꾸었습니다!

그러나 건축가는 이것을 위해 EntityFramework 라이브러리를 사용하지 말라고 말했습니다! 죄송합니다! 드라마! 그런 다음 맞춤 작업 구현 (yuk)을 만들었습니다. 당신은 어떻게 알고 있습니다. 아직도 쉬워요! .. 아직도 ..

다음에 어디? ASP.Net Core에서 동기 호출을 비동기로 변환 에 대해 볼 수있는 멋진 비디오가 있습니다 . 아마도 이것을 읽은 후 갈 방향 일 것입니다.


환상적인 답변! 이것은 나를 도와주었습니다
cklimowski

1
좋은 대답입니다. (a) ".Net framework 4.2"에 대한 언급 (내가 아는 그런 버전은 존재하지 않음) (b) EntityFrameWork => EntityFramework
immitev

15

여기에있는 모든 대답은 Task.Delay()다른 내장 async기능을 사용합니다. 그러나 다음은 그러한 async기능을 전혀 사용하지 않는 예입니다 .

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
감사합니다! 실제로 기다리지 않고 실제로 작동하는 첫 번째 답변.
Jeffnl

보여 주셔서 감사합니다. task.Wait();async / await 지옥을 피하는 방법 : P
encoder

12

이 답변은 ASP.NET에 특정한 정보를 제공하는 것을 목표로합니다.

MVC 컨트롤러에서 async / await를 사용하면 아래 기사에 설명 된대로 스레드 풀 사용률을 높이고 처리량을 훨씬 향상시킬 수 있습니다.

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

시작시 많은 수의 동시 요청을 보거나 버스트가 급증하는 (동시성이 갑자기 증가하는) 웹 응용 프로그램에서 이러한 웹 서비스 호출을 비동기로 만들면 응용 프로그램의 응답 성이 향상됩니다. 비동기 요청은 동기 요청과 동일한 시간이 소요됩니다. 예를 들어, 요청이 완료하는 데 2 ​​초가 걸리는 웹 서비스 호출을하는 경우 요청은 동기식 또는 비동기식으로 수행 되든 2 초가 걸립니다. 그러나 비동기 호출 중에 스레드는 첫 번째 요청이 완료되기를 기다리는 동안 다른 요청에 응답하는 것을 차단하지 않습니다. 따라서 비동기 요청은 장기 실행 작업을 호출하는 동시 요청이 많은 경우 요청 큐 및 스레드 풀 증가를 방지합니다.


12

비동기 및 대기 간단한 설명

간단한 유추

아침 기차를 기다리는 사람이있을 수 있습니다 . 이것이 그들이 현재 수행하고있는 그들의 주요 작업이기 때문에 이것이 그들이하는 전부입니다. (동기식 프로그래밍 (일반적으로 수행하는 작업!))

담배를 피우고 커피를 마시는 동안 다른 사람이 아침 기차를 기다릴있습니다 . (비동기 프로그래밍)

비동기 프로그래밍이란 무엇입니까?

비동기 프로그래밍은 프로그래머가 자신의 코드 중 일부를 기본 실행 스레드와 별도의 스레드에서 실행 한 다음 기본 스레드에 완료를 알리도록 선택하는 곳입니다.

비동기 키워드는 실제로 무엇을합니까?

async 키워드 앞에 다음과 같은 메소드 이름을 붙입니다.

async void DoSomething(){ . . .

프로그래머는 비동기 작업을 호출 할 때 await 키워드를 사용할 수 있습니다. 그게 다야.

이것이 왜 중요한가?

많은 소프트웨어 시스템에서 메인 스레드는 특히 사용자 인터페이스와 관련된 작업을 위해 예약되어 있습니다. 컴퓨터에서 완료하는 데 5 초가 걸리는 매우 복잡한 재귀 알고리즘을 실행하고 있지만 메인 스레드 (UI 스레드)에서 실행하는 경우 사용자가 내 응용 프로그램에서 아무것도 클릭하려고하면 고정 된 것처럼 보입니다. 내 주요 스레드가 대기열에 있고 현재 너무 많은 작업을 처리하고 있습니다. 결과적으로 메인 스레드는 마우스 클릭을 처리하여 버튼 클릭에서 메소드를 실행할 수 없습니다.

Async와 Await는 언제 사용합니까?

사용자 인터페이스와 관련이없는 작업을 수행 할 때는 이상적으로 비동기 키워드를 사용하십시오.

사용자가 휴대 전화에서 스케치 할 수 있지만 5 초마다 인터넷의 날씨를 확인하는 프로그램을 작성한다고 가정 해 보겠습니다.

응용 프로그램의 사용자가 모바일 터치 스크린과 계속 상호 작용하여 예쁜 그림을 그릴 때 날씨를 얻으려면 5 초마다 네트워크에 대한 폴링 호출을 기다리고 있어야합니다.

비동기 및 대기 사용 방법

위의 예에서 다음은 작성 방법에 대한 의사 코드입니다.

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

추가 사항-업데이트

C #에서는 Tasks에 싸여있는 메소드 만 기다릴 수 있다는 것을 원래 메모에서 언급하지 않았습니다. 예를 들어이 방법을 기다릴 수 있습니다.

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

다음과 같은 작업이 아닌 메소드를 기다릴 수 없습니다.

async string FetchHelloWorld() {..

여기서 Task 클래스의 소스 코드를 검토 하십시오 .


4
시간을내어 작성해 주셔서 감사합니다.
Prashant

10

비동기 / 대기

실제로 Async / Await는 비동기 작업의 콜백을 만들기위한 구문 설탕 인 키워드 쌍입니다.

이 작업을 예로 들어 보겠습니다.

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

위의 코드에는 몇 가지 단점이 있습니다. 오류가 전달되지 않고 읽기가 어렵습니다. 그러나 Async와 Await는 우리를 돕기 위해 들어옵니다.

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

대기 호출은 비동기 메서드에 있어야합니다. 여기에는 몇 가지 장점이 있습니다.

  • 작업 결과를 반환
  • 콜백 자동 생성
  • 오류 확인 및 콜 스택에서 버블 링 가능
  • 결과를 기다립니다
  • 메인 스레드를 해제
  • 메인 스레드에서 콜백을 실행합니다.
  • 스레드 풀에서 작업자 스레드를 사용하여 작업
  • 코드를 읽기 쉽게 만듭니다
  • 그리고 훨씬 더

참고 : Async 및 Await는 비동기 호출 과 함께 사용 되지 않습니다 . 이를 위해 Task.Run ()과 같은 작업 라이브러리 를 사용해야 합니다.

다음은 await와 none await 솔루션의 비교입니다.

이것은 비동기 솔루션입니다.

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

이것은 비동기 방법입니다.

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

실제로 await 키워드없이 async 메서드를 호출 할 수 있지만 여기서는 예외가 해제 모드에서 삼킨다는 것을 의미합니다.

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

비동기 및 대기는 병렬 컴퓨팅을위한 것이 아닙니다. 그들은 주요 스레드를 차단하지 않는 데 사용됩니다. asp.net 또는 Windows 응용 프로그램과 관련하여 네트워크 호출로 인해 메인 스레드를 차단하는 것은 나쁜 일입니다. 이렇게하면 앱이 응답하지 않거나 충돌 할 수 있습니다.

더 많은 예제를 보려면 ms 문서 를 확인하십시오 .


9

솔직히 말해서 나는 여전히 가장 좋은 설명은 Wikipedia에 대한 미래와 약속에 관한 것이라고 생각합니다. http://en.wikipedia.org/wiki/Futures_and_promises

기본 아이디어는 작업을 비동기 적으로 실행하는 별도의 스레드 풀이 있다는 것입니다. 그것을 사용할 때. 그러나 개체는 작업을 한 번에 실행하고 요청하면 결과를 제공한다는 약속을합니다. 이것은 결과를 요청할 때 차단되고 완료되지 않았지만 그렇지 않으면 스레드 풀에서 실행됨을 의미합니다.

여기에서 작업을 최적화 할 수 있습니다. 일부 작업은 비동기 적으로 구현할 수 있으며 후속 요청을 일괄 처리하거나 순서를 다시 지정하여 파일 IO 및 네트워크 통신과 같은 작업을 최적화 할 수 있습니다. 이것이 이미 Microsoft의 작업 프레임 워크에 있는지 확실하지 않지만 그것이 아닌 경우 추가해야 할 첫 번째 것 중 하나입니다.

실제로 C # 4.0의 수율로 미래 패턴 정렬을 구현할 수 있습니다. 정확히 어떻게 작동하는지 알고 싶다면 http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/과 같은 적절한 작업을 수행하는이 링크를 추천 할 수 있습니다 . 그러나 직접 게임을 시작하면 Microsoft가했던 것과 같은 멋진 일을하려면 언어 지원이 필요하다는 것을 알게 될 것입니다.


8

간단한 콘솔 응용 프로그램 을 실행 하려면 이 바이올린 https://dotnetfiddle.net/VhZdLU를 참조하십시오 (가능하면 개선하십시오).동일한 프로그램에서 Task, Task.WaitAll (), async 및 await 연산자의 사용법을 보여주는 .

이 바이올린은 실행주기 개념을 분명히해야합니다.

다음은 샘플 코드입니다

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

출력 창에서 나오는 추적 : 여기에 이미지 설명을 입력하십시오


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

더 높은 수준에서 :

1) 비동기 키워드를 사용하면 기다릴 수 있으며 그게 전부입니다. 비동기 키워드는 별도의 스레드에서 메소드를 실행하지 않습니다. 시작 f 비동기 메소드는 시간이 많이 걸리는 작업에서 대기 할 때까지 동기식으로 실행됩니다.

2) 유형이 T 인 태스크 또는 태스크를 리턴하는 메소드를 기다릴 수 있습니다. 비동기 void 메소드를 기다릴 수 없습니다.

3) 메인 스레드가 만나는 순간 시간이 걸리는 작업을 기다리거나 실제 작업이 시작되면 메인 스레드는 현재 메소드의 호출자로 돌아갑니다.

4) 메인 스레드가 여전히 실행중인 작업을 기다리는 경우, 기다리지 않고 현재 메소드의 호출자로 돌아갑니다. 이런 식으로 응용 프로그램은 응답 상태를 유지합니다.

5) 처리 작업을 기다립니다. 이제 스레드 풀과 별도의 스레드에서 실행됩니다.

6)이 작업을 완료하면 아래의 모든 코드가 별도의 스레드에서 실행됩니다.

다음은 샘플 코드입니다. 그것을 실행하고 스레드 ID를 확인하십시오

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

내가 이해하는 방식에도 믹스에 세 번째 용어가 추가되어야합니다 Task.

Async 비동기 메소드라고 말하기 위해 메소드에 넣은 한정자입니다.

Taskasync함수 의 반환입니다 . 비동기 적으로 실행됩니다.

당신 await은 작업입니다. 코드 실행이이 라인에 도달하면 컨트롤은 주변 원래 함수의 호출자에게 다시 점프합니다.

대신, 당신은의 반환에 할당 async기능 (즉, Task코드 실행이 줄에 도달하면, 그냥 변수로를) 계속 주변 기능에 그 선을지나 잠시Task 실행하는 비동기.


1

지속 시간 논리를 수행하기 위해 백그라운드 스레드를 생성하는 것과 동일합니까?

이 기사에서는 async and await (C #)를 사용한 MDSN : Asynchronous Programming에 대해 설명합니다.

async 및 await 키워드는 추가 스레드를 생성하지 않습니다. 비동기 메서드는 자체 스레드에서 실행되지 않으므로 비동기 메서드에는 멀티 스레딩이 필요하지 않습니다. 메소드는 현재 동기화 컨텍스트에서 실행되며 메소드가 활성화 된 경우에만 스레드에서 시간을 사용합니다.


1

다음 코드에서 HttpClient 메서드 GetByteArrayAsync는 작업 getContentsTask를 반환합니다. 이 작업은 작업이 완료되면 실제 바이트 배열을 생성하는 약속입니다. await 연산자는 getContentsTask가 완료 될 때까지 SumPageSizesAsync에서 실행을 일시 중단하기 위해 getContentsTask에 적용됩니다. 그 동안 SumPageSizesAsync 호출자에게 제어가 리턴됩니다. getContentsTask가 완료되면 await 표현식은 바이트 배열로 평가됩니다.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

아래는 대화 상자를 열어 Excel 파일을 읽은 다음 async를 사용하고 Excel에서 한 줄씩 하나씩 읽고 그리드에 바인딩하는 코드를 비동기 실행을 기다리는 코드입니다

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

여기의 답변은 대기 / 비동기에 대한 일반적인 지침으로 유용합니다. 또한 대기 / 비동기 연결 방법에 대한 세부 정보도 포함합니다. 이 디자인 패턴을 사용하기 전에 알아야 할 실제 경험을 공유하고 싶습니다.

"await"라는 용어는 문자 그대로이므로 호출하는 스레드는 계속하기 전에 메소드의 결과를 기다립니다. 온 전경 스레드, 이것은이다 재해 . 포 그라운드 스레드는 뷰, 뷰 모델, 초기 애니메이션 및 이러한 요소로 부팅 된 다른 모든 것을 포함하여 앱을 구성해야하는 부담이 있습니다. 포 그라운드 스레드를 기다리면 중지합니다. 앱 됩니다. 아무 일도 일어나지 않으면 사용자는 기다립니다. 이것은 부정적인 사용자 경험을 제공합니다.

다양한 방법을 사용하여 백그라운드 스레드를 확실히 기다릴 수 있습니다.

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

이러한 설명에 대한 전체 코드는 https://github.com/marcusts/xamarin-forms-annoyances에 있습니다. AwaitAsyncAntipattern.sln이라는 솔루션을 참조하십시오.

GitHub 사이트는이 주제에 대한보다 자세한 토론 링크를 제공합니다.


1
내가 이해하는 것에서 async / await콜백의 구문 설탕은 스레딩과 관련이 없습니다. msdn.microsoft.com/en-us/magazine/hh456401.aspx 비 CPU 바인딩 코드 (예 : 입력 대기 또는 지연)입니다. Task.RunCPU- 바운드 코드 blog.stephencleary.com/2013/10/
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.이것은 사실이 아닙니다-아마도 Task.Wait ()을 의미합니까? 를 사용 await하면 대기중인 항목이 완료 될 때 실행될 나머지 메소드가 연속으로 설정됩니다. 사용한 메소드를 종료하므로 호출자가 계속할 수 있습니다. 그런 다음 대기 라인이 실제로 완료되면 일부 스레드 (보통 작업자 스레드)에서 해당 메소드의 나머지를 마무리합니다.
돈 Cheadle

@geometrikal의 핵심 async/await은 .NET 스레드를 해제하는 것 입니다. 당신이하는 경우 await(예 : .NET의 File.WriteAsync 등) 진정한-비동기 작업이,이 방법의 나머지 부분을 중지하면 사용 await발신자가 계속 잠재적으로 그 목적을 완료 할 수 있도록한다. 스레드 차단 또는 await-ed 작업을 기다리는 중이 없습니다 . 작업 await이 완료되면 나머지 async/await메소드가 스레드에 배치되어 실행됩니다 (콜백 아이디어와 유사).
Don Cheadle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.