[C#] ‘async’및 ‘await’사용 방법 및시기

내가 이해 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;
}



답변

사용하는 경우 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).


답변

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

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

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

전혀.

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

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

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

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

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

아니.


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


답변

설명

다음은 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이미.


답변

다른 답변들보다 더 기다려보십시오 (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";
}


답변

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

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의 다른 스레드가이 컨텍스트를 선택하고 최종 메시지를 표시합니다.

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


답변

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

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

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

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


답변

다음은 팔로우하는 사람들에게 명확하게 알려주는 빠른 콘솔 프로그램입니다. 이 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);
        }
    }
}