code

"대기 가능한"메서드를 작성하는 방법은 무엇입니까?

codestyles 2020. 12. 1. 08:02
반응형

"대기 가능한"메서드를 작성하는 방법은 무엇입니까?


나는 마지막으로 예를 들면 종류의 "수"의 I,하지만 모든 예제 내가 닷넷 프레임 워크의 방법 비동기 호출을 본 적이 비동기 및 await를 키워드로 찾고 있어요 이 하나의 호출 HttpClient.GetStringAsync().

내가 그다지 명확하지 않은 것은 그러한 방법에서 무슨 일이 일어나는지, 그리고 내가 "기다릴 수있는"방법을 어떻게 작성하는지에 관한 것이다. Task에서 비동기 적으로 실행하려는 코드를 래핑하고 반환하는 것만 큼 간단합니까?


간단합니다.

Task.Run(() => ExpensiveTask());

대기 가능한 메서드로 만들려면 :

public Task ExpensiveTaskAsync()
{
    return Task.Run(() => ExpensiveTask());
}

여기서 중요한 것은 작업을 반환하는 것입니다. 메서드는 비동기로 표시 할 필요조차 없습니다. (사진에 나오려면 조금 더 읽으십시오)

이제 이것은 다음과 같이 부를 수 있습니다.

async public void DoStuff()
{
    PrepareExpensiveTask();
    await ExpensiveTaskAsync();
    UseResultsOfExpensiveTask();
}

async메서드가 반환 될 때까지 호출자에게 제어를 반환 할 수 있기 때문에 여기에서 메서드 서명이이라고 말합니다 ExpensiveTaskAsync(). 또한이 경우 비용이 많이 들기 때문에 웹 요청과 같이 시간이 많이 걸립니다. 무거운 계산을 다른 스레드로 보내려면 일반적으로 "이전"접근 방식을 사용하는 것이 좋습니다 (예 System.ComponentModel.BackgroundWorker: GUI 응용 프로그램 또는 System.Threading.Thread.


내 자신의 "대기 가능한"메서드를 어떻게 작성합니까? 비동기 적으로 실행하려는 코드를 a에서 래핑 Task하고 반환하는 것만 큼 ​​간단 합니까?

이것은 하나의 옵션이지만 실제로 비동기 코드의 많은 이점을 제공하지 않기 때문에 원하는 작업이 아닐 가능성이 큽니다. 자세한 내용은 Stephen Toub의 동기 메서드에 대한 비동기 래퍼를 노출해야합니까?를 참조하십시오 .

일반적으로 메소드는 기다릴 수 없으며 유형 은 있습니다. 당신이 뭔가를 쓸 수 있도록하려면 await MyMethod(), 다음 MyMethod()반환하는 Task, Task<T>또는 사용자 정의 await할 수 유형입니다. 사용자 정의 유형을 사용하는 것은 드문 고급 시나리오입니다. 를 사용 Task하면 몇 가지 옵션이 있습니다.

  • async및을 사용하여 방법을 작성하십시오 await. 이것은 비동기 적으로 작업 작성하는 데 유용 하지만 가장 안쪽의 await가능한 호출 에는 사용할 수 없습니다 .
  • , like 또는 Task에 대한 방법 중 하나를 사용 하여을 만듭니다 .TaskTask.Run()Task.FromAsync()
  • 사용 TaskCompletionSource. 이것은 가장 일반적인 접근 방식이며 await미래에 일어날 모든 것에서 가능한 방법 을 만드는 데 사용할 수 있습니다 .

... 내 자신의 "기다릴 수있는"방법을 작성하는 방법.

a를 반환하는 Task것이 유일한 방법은 아닙니다. 사용자 지정 대기자 를 생성 할 수있는 옵션이 있습니다 ( 및를 구현 GetAwaiter하여 INotifyCompletion). 여기에 훌륭한 읽기 : " Await any "가 있습니다. 사용자 지정 대기자를 반환하는 .NET API의 예 : Task.Yield(), Dispatcher.InvokeAsync.

여기여기에 사용자 지정 대기자가있는 게시물이 있습니다 . 예 :

// don't use this in production
public static class SwitchContext
{
    public static Awaiter Yield() { return new Awaiter(); }

    public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
    {
        public Awaiter GetAwaiter() { return this; }

        public bool IsCompleted { get { return false; } }

        public void OnCompleted(Action continuation)
        {
            ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
        }

        public void GetResult() { }
    }
}

// ...

await SwitchContext.Yield();

예, 기술적으로 당신은 단지를 반환해야 Task또는 Task<Result>에서 asyncawaitable 방법을 구현하는 방법.

이것은 Task-Based Asynchronous Pattern을 지원합니다 .

그러나 TAP를 구현하는 방법에는 여러 가지가 있습니다. 자세한 내용 은 작업 기반 비동기 패턴 구현 을 참조하세요.

(그러나이 모든 구현은 여전히 반환 Task또는 Task<Result>물론.)


방법을 Task로 변환하십시오. @Romiox처럼 나는 일반적 으로이 확장자를 사용합니다.

 public static partial class Ext
{
    #region Public Methods
     public static Task ToTask(Action action)
    {
        return Task.Run(action);
    }
    public static Task<T> ToTask<T>(Func<T> function)
    {
        return Task.Run(function);
    }
    public static async Task ToTaskAsync(Action action)
    {
        await Task.Run(action);
    }
    public static async Task<T> ToTaskAsync<T>(Func<T> function)
    {
        return await Task.Run(function);
    }
    #endregion Public Methods
}

이제 우리는 당신이

무효 foo1 ()

무효 foo2 (int i1)

int foo3 ()

int foo4 (int i1)

... 그런 다음 @Romiox와 같은 [비동기 메서드]를 선언 할 수 있습니다.

async Task foo1Async(){
   return await Ext.ToTask(()=>foo1());
}
async Task foo2Async(int i1){
   return await Ext.ToTask(()=>foo2(i1));
}
async Task<int> foo3Async(){
   return await Ext.ToTask(()=>foo3());
}
async Task<int> foo4Async(int i1){
    return await Ext.ToTask(()=>foo4(i1));
}

또는

async Task foo1Async(){
 return await Ext.ToTaskAsync(()=>foo1());
}
async Task foo2Async(int i1){
return await Ext.ToTaskAsync(()=>foo2(i1));
}
async Task<int> foo3Async(){
return await Ext.ToTaskAsync(()=>foo3());
}
async Task<int> foo4Async(int i1){
return await Ext.ToTaskAsync(()=>foo4(i1));
}

...

이제 async를 사용하고 fooAsync 예를 들어 foo4Async를 기다립니다.

 async Task<int> TestAsync()
{
   ///Initial Code
   int m=3;
   ///Call the task
   var X =foo4Async(m);
   ///Between
   ///Do something while waiting comes here
   ///..
   var Result =await X;
   ///Final
   ///Some Code here
   return Result;
}

를 사용하지 않으려면 Task완전히 사용자 정의 된 awaitable 객체를 작성할 수 있습니다. 이러한 객체는 객체 자체가 될 수 GetAwaiter ()있는를 구현하는 객체를 반환하는 메서드를 구현하는 INotifyCompletion것입니다.

더보기 : INotifyCompletion

awaiter는 다음을 구현합니다.

  • IsCompleted 상태를 얻는 것입니다
  • GetResult () 결과를 얻기 위해
  • OnCompleted (Action continuation) 연속 대리자를 설정합니다.

The awaitable object contains some method for actual payload (e.g. below, the method is Run).

class Program {
    // Need to change the declaration of Main() in order to use 'await'
    static async Task Main () {
        // Create a custom awaitable object
        MyAwaitable awaitable = new MyAwaitable ();

        // Run awaitable payload, ignore returned Task
        _ = awaitable.Run ();

        // Do some other tasks while awaitable is running
        Console.WriteLine ("Waiting for completion...");

        // Wait for completion
        await awaitable;

        Console.WriteLine ("The long operation is now complete. " + awaitable.GetResult());
    }
}

public class MyAwaitable : INotifyCompletion {
    // Fields
    private Action continuation = null;
    private string result = string.Empty;

    // Make this class awaitable
    public MyAwaitable GetAwaiter () { return this; }

    // Implementation of INotifyCompletion for the self-awaiter
    public bool IsCompleted { get; set; }
    public string GetResult () { return result; }
    public void OnCompleted (Action continuation) {
        // Store continuation delegate
        this.continuation = continuation;
        Console.WriteLine ("Continuation set");
    }

    // Payload to run
    public async Task Run () {
        Console.WriteLine ("Computing result...");

        // Wait 2 seconds
        await Task.Delay (2000);
        result = "The result is 10";

        // Set completed
        IsCompleted = true;

        Console.WriteLine ("Result available");

        // Continue with the continuation provided
        continuation?.Invoke ();
    }
}

참고URL : https://stackoverflow.com/questions/21700846/how-to-write-an-awaitable-method

반응형