Programing

'async'및 'await'를 사용하는 방법과시기

lottogame 2020. 9. 28. 07:57
반응형

'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. 독립적 인 작업은 메인 스레드 (Thread ID = 1) await longRunningTask에 도달 했다고 가정합니다 .

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

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


내 이해에서 비동기 및 대기가 수행하는 주요 작업 중 하나는 코드를 작성하고 읽기 쉽게 만드는 것입니다.

비동기 코드를 작성하고 읽기 쉽게 만드는 것 입니다.

장기간 논리를 수행하기 위해 백그라운드 스레드를 생성하는 것과 동일한가요?

전혀.

//이 메서드가 'async'로 표시되어야하는 이유를 이해할 수 없습니다.

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

//이 줄은 DoSomethingAsync () 메서드에서 5 초 동안 휴면 후 도달합니다. 즉시 도달해야하지 않습니까?

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

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

아니.


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


다른 답변 외에도 await (C # 참조)를 살펴보십시오.

더 구체적으로 포함 된 예제에서 귀하의 상황을 약간 설명합니다.

다음 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";
}

설명

다음은 높은 수준에서 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에 대한 기술적 이해가있는 사람을 대상으로하는 Stephen Cleary의 설명으로 이동하십시오.


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

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. Main은 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"메서드는 비 동기화하려는 장기 실행 메서드입니다. Async를 실행하는 것은 TestAsync 메서드에 의해 수행됩니다. 테스트 루프 메서드는 "TaskToDo"작업을 통해 실행되고 Async로 실행됩니다. 실행마다 동일한 순서로 완료되지 않기 때문에 결과에서 확인할 수 있습니다. 완료되면 콘솔 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);
        }
    }
}

이 답변은 ASP.NET과 관련된 몇 가지 정보를 제공하는 것을 목표로합니다.

MVC 컨트롤러에서 async / await를 활용하면 스레드 풀 활용도를 높이고 아래 기사에 설명 된대로 훨씬 더 나은 처리량을 달성 할 수 있습니다.

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

시작시 많은 수의 동시 요청을 보거나 버스트로드 (동시성이 갑자기 증가하는 경우)가있는 웹 애플리케이션에서 이러한 웹 서비스 호출을 비동기식으로 만들면 애플리케이션의 응답 성이 증가합니다. 비동기 요청은 동기 요청과 처리하는 데 동일한 시간이 걸립니다. 예를 들어 요청이 완료하는 데 2 ​​초가 필요한 웹 서비스 호출을 수행하는 경우 요청이 동기식으로 수행 되든 비동기 적으로 수행 되든 2 초가 걸립니다. 그러나 비동기 호출 중에 스레드는 첫 번째 요청이 완료 될 때까지 기다리는 동안 다른 요청에 대한 응답이 차단되지 않습니다. 따라서 비동기 요청은 장기 실행 작업을 호출하는 동시 요청이 많을 때 요청 대기열 및 스레드 풀 증가를 방지합니다.


여기의 모든 대답은 Task.Delay () 또는 다른 내장 비동기 함수를 사용합니다. 그러나 다음은 이러한 비동기 함수를 사용하지 않는 내 예입니다.

    // Starts counting to a large numbewr 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;
    }

솔직히 말해서 나는 여전히 가장 좋은 설명은 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가 한 일입니다.


비동기 / 대기

실제로 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");

    }

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

  • Task의 결과를 반환합니다.
  • 자동으로 콜백 생성
  • 오류를 확인하고 콜 스택에서 버블 링 할 수 있습니다 (콜 스택에서 호출을 기다리지 않는 경우에만)
  • 결과를 기다린다
  • 메인 스레드를 해제합니다.
  • 메인 스레드에서 콜백을 실행합니다.
  • 작업을 위해 스레드 풀의 작업자 스레드를 사용합니다.
  • 코드를 읽기 쉽게
  • 그리고 훨씬 더

참고 : Async 및 Await는이를 수행 하지 않는 비동기 호출 과 함께 사용 됩니다 . 당신은 사용해야하는 작업 Libary 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 키워드없이 비동기 메서드를 호출 할 수 있지만 이는 여기에있는 모든 예외가 릴리스 모드에서 삼켜진다는 것을 의미합니다.

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

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

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


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

이 바이올린은 실행주기 개념을 지워야합니다.

다음은 샘플 코드입니다.

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 분

  • 질문 자기 성찰 (배운 술) : 1 분

  • 구문 설탕을 빠르게 통과 : 5 분

  • 개발자의 혼란을 공유하세요 : 5 분

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

  • 다음은 어디?

메서드 실행 흐름 이해 (다이어그램 포함) : 3 분

이 이미지에서는 # 6에 초점을 맞 춥니 다. 여기에 이미지 설명 입력

# 6 단계에서 : AccessTheWebAsync ()는 getStringTask의 결과없이 수행 할 수있는 작업이 부족합니다. 따라서 AccessTheWebAsync는 await 연산자를 사용하여 진행을 일시 중단하고 호출자에게 제어권을 되돌립니다 (yield). AccessTheWebAsync는 호출자에게 Task (문자열 반환 값)를 반환합니다. 작업은 문자열 결과를 생성하겠다는 약속을 나타냅니다. 하지만 언제 전화를받을까요? 또 두 번째 전화?

AccessTheWebAsync ()의 호출자는 대기만했습니다 (일부 내부 작업을 수행 한 다음 필요한 경우 기다릴 수 있음). 따라서 호출자는 AccessTheWebAsync를 기다리고 있고 AccessTheWebAsync는 현재 GetStringAsync를 기다리고 있습니다.

메서드가 이미 반환되었으므로 다시 반환 할 수 없습니다 (두 번째 없음). 그러면 발신자는 어떻게 알 수 있습니까? 작업 에 관한 모든 것입니다 ! 작업이 반환되었습니다. 작업을 기다렸습니다 (방법이 아니라 값). 태스크에서 값이 설정됩니다. 작업 상태가 완료로 설정됩니다. 발신자는 작업 만 모니터링합니다. 나중에 여기에서 더 읽을 수 있습니다 .

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

질문을 약간 조정 해 보겠습니다.

사용 방법 및시기 ? asyncawait Tasks

학습은 Task자동으로 다른 2를 다루기 때문에 적어도 학습을 위해서. 물론 이것은 async에 대한 귀하의 질문에 대한 답변 await입니다.

구문 설탕을 빠르게 통과 : 5 분

  • 변환 전 (원래 방식)

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

  • 위의 메서드를 호출하는 또 다른 Task-ified 메서드

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

await 또는 async를 언급 했습니까? 아니요. 위의 메서드를 호출하면 작업을받습니다. 모니터링 할 수 있습니다. 작업이 무엇을 반환하는지 이미 알고 있습니다. 정수.

  • Task를 호출하는 것은 약간 까다 롭습니다. MethodTask ()를 호출합시다.

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

우리는 작업이 완료되기를 '대기'하고 있습니다. 따라서 await. await를 사용하므로 'Async'를 접두사로 사용하여 async (필수) 및 MethodAsync를 사용해야합니다 (코딩 표준). 나중에 여기에서 읽을 추가 정보

개발자의 혼란을 공유하세요 : 5 분

개발자가 구현하지 않는 실수를 Task했지만 여전히 작동합니다! 질문을 이해하고 여기에 제공된 답변 만 확인 하십시오 . 읽고 완전히 이해 하셨기를 바랍니다. 마찬가지로 우리의 예제에서 이미 빌드 된 것을 호출하는 MethodAsync()것이 Task( MethodTask()) 우리 자신으로 그 메소드를 구현하는 것보다 훨씬 쉽습니다 . 대부분의 개발자 Tasks는 코드를 비동기 코드로 변환하는 동안 머리 를 돌리는 것이 어렵습니다 .

팁 : 어려움을 아웃소싱하기 위해 기존의 비동기 구현 ( MethodAsync또는 ToListAsync)을 찾으십시오 . 그래서 우리는 Async를 다루고 기다릴 필요가 있습니다 (일반 코드와 매우 유사하고 쉽습니다).

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

데이터 영역에서 아래 표시된 코드 줄이 끊어지기 시작했습니다 (여러 곳). .Net framework 4.2에서 .Net core로 일부 코드를 업데이트했기 때문입니다. 응용 프로그램 전체에서 1 시간 만에이 문제를 해결해야했습니다!

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

쉬워요!

  1. EntityFrameWork 너겟 (QueryableExtensions가 있음)
  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 라이브러리를 사용하지 말라고했습니다! 이런! 드라마! 그런 다음 사용자 지정 작업 구현을 만들었습니다. 당신이 방법을 알고 있습니다. 여전히 쉽습니다!

다음은 어디? ASP.Net Core에서 동기 호출을 비동기로 변환 하는 방법에 대한 멋진 빠른 비디오가 있습니다.이 내용을 읽은 후 방향이 될 가능성이 높기 때문입니다.


비동기 및 대기 간단한 설명

간단한 비유

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

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

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

비동기 프로그래밍은 프로그래머가 메인 실행 스레드와 별도의 스레드에서 일부 코드를 실행 한 다음 완료되면 메인 스레드에 알리는 곳입니다.

async 키워드는 실제로 무엇을합니까?

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

async void DoSomething(){ . . .

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

이것이 왜 중요한가요?

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

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

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

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

애플리케이션 사용자가 예쁜 그림을 그리기 위해 모바일 터치 스크린과 계속 상호 작용해야하므로 날씨를 알기 위해 5 초마다 네트워크에 폴링 호출이 호출 될 때까지 기다려야합니다.

Async 및 Await를 사용하는 방법

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

     //ASYNCHRONOUS
    //this is called every 5 seconds
    async void 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();
    }

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

더 높은 수준에서 :

1) Async 키워드는 await를 활성화하고 그게 전부입니다. Async 키워드는 별도의 스레드에서 메서드를 실행하지 않습니다. 시작 f async 메서드는 시간이 많이 걸리는 작업에서 await에 도달 할 때까지 동 기적으로 실행됩니다.

2) T 유형의 Task 또는 Task를 반환하는 메서드를 기다릴 수 있습니다. async void 메서드에서는 기다릴 수 없습니다.

3) 메인 스레드가 시간 소모적 인 작업을 기다리는 순간 또는 실제 작업이 시작되면 메인 스레드는 현재 메서드의 호출자에게 반환됩니다.

4) 메인 스레드가 아직 실행중인 작업에서 await를 확인하면이를 기다리지 않고 현재 메서드의 호출자에게 반환합니다. 이런 식으로 응용 프로그램은 계속 응답합니다.

5) Await on processing task, 이제 스레드 풀과 별도의 스레드에서 실행됩니다.

6)이 await 작업이 완료되면 그 아래의 모든 코드는 별도의 스레드에서 실행됩니다.

아래는 샘플 코드입니다. 그것을 실행하고 스레드 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();
        }

    }
}

내가 이해하는 방식은 또한 혼합에 세 번째 용어가 추가되어야합니다 Task.

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

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

당신 await은 과제입니다. 코드 실행이이 줄에 도달하면 제어가 주변의 원래 함수 호출자에게 다시 뛰어납니다.

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


장기간 논리를 수행하기 위해 백그라운드 스레드를 생성하는 것과 동일하게 사용하고 있습니까?

이 문서에서는 MDSN : 비동기 및 대기 (C #)를 사용한 비동기 프로그래밍에 대해 명시 적으로 설명합니다.

async 및 await 키워드로 인해 추가 스레드가 생성되지 않습니다. 비동기 메서드는 자체 스레드에서 실행되지 않으므로 비동기 메서드에는 다중 스레딩이 필요하지 않습니다. 메서드는 현재 동기화 컨텍스트에서 실행되며 메서드가 활성 상태 일 때만 스레드에서 시간을 사용합니다.


다음 코드에서 HttpClient 메서드 GetByteArrayAsync는 Task, getContentsTask를 반환합니다. 이 작업은 작업이 완료 될 때 실제 바이트 배열을 생성한다는 약속입니다. await 연산자는 getContentsTask에 적용되어 getContentsTask가 완료 될 때까지 SumPageSizesAsync에서 실행을 일시 중단합니다. 그 동안 제어는 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);
    // . . .
}

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

"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 사이트는이 주제에 대한 더 자세한 토론에 대한 링크도 제공합니다.


아래는 대화 상자를 열어 엑셀 파일을 읽은 다음 비동기를 사용하고 비동기 실행을 기다리는 코드입니다. 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
    {
    }
}

참고 URL : https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await

반응형