[C#] C #에서 비동기 메소드를 어떻게 작성합니까?

내가 읽은 모든 블로그 게시물은 C #에서 비동기 메서드를 사용하는 방법을 알려 주지만 이상한 이유로 소비 할 자체 비동기 메서드를 작성하는 방법을 설명하지 마십시오. 그래서 지금 내 코드를 사용하는이 코드가 있습니다.

private async void button1_Click(object sender, EventArgs e)
{
    var now = await CountToAsync(1000);
    label1.Text = now.ToString();
}

그리고 나는이 방법을 썼습니다 CountToAsync:

private Task<DateTime> CountToAsync(int num = 1000)
{
    return Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < num; i++)
        {
            Console.WriteLine("#{0}", i);
        }
    }).ContinueWith(x => DateTime.Now);
}

이것이 Task.Factory비동기 메소드를 작성하는 가장 좋은 방법입니까, 아니면 다른 방법으로 작성해야합니까?



답변

StartNew복잡한 수준이 필요 하지 않으면 권장하지 않습니다 .

비동기 메소드가 다른 비동기 메소드에 종속되는 경우 가장 쉬운 방법은 async키워드 를 사용하는 것입니다 .

private static async Task<DateTime> CountToAsync(int num = 10)
{
  for (int i = 0; i < num; i++)
  {
    await Task.Delay(TimeSpan.FromSeconds(1));
  }

  return DateTime.Now;
}

비동기 메소드가 CPU 작업을 수행하는 경우 다음을 사용해야합니다 Task.Run.

private static async Task<DateTime> CountToAsync(int num = 10)
{
  await Task.Run(() => ...);
  return DateTime.Now;
}

async/ await소개가 도움 이 될 수 있습니다.


답변

메소드 내에서 async / await를 사용하고 싶지 않지만 외부에서 await 키워드를 사용할 수 있도록 여전히 “장식”하면 TaskCompletionSource.cs :

public static Task<T> RunAsync<T>(Func<T> function)
{
    if (function == null) throw new ArgumentNullException(“function”);
    var tcs = new TaskCompletionSource<T>();
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try
        {
           T result = function();
           tcs.SetResult(result);
        }
        catch(Exception exc) { tcs.SetException(exc); }
   });
   return tcs.Task;
}

여기여기에서

Tasks와 같은 패러다임을 지원하려면 Task façade를 유지하는 방법과 임의의 비동기 작업을 Task로 참조하는 기능이 필요하지만 기본 인프라의 규칙에 따라 해당 Task의 수명을 제어해야합니다. 비 동시성이고, 비용이 많이 들지 않는 방식으로 그렇게하는 것. 이것이 TaskCompletionSource의 목적입니다.

예를 들어 .NET 소스에서도 사용됩니다. WebClient.cs :

    [HostProtection(ExternalThreading = true)]
    [ComVisible(false)]
    public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
    {
        // Create the task to be returned
        var tcs = new TaskCompletionSource<string>(address);

        // Setup the callback event handler
        UploadStringCompletedEventHandler handler = null;
        handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
        this.UploadStringCompleted += handler;

        // Start the async operation.
        try { this.UploadStringAsync(address, method, data, tcs); }
        catch
        {
            this.UploadStringCompleted -= handler;
            throw;
        }

        // Return the task that represents the async operation
        return tcs.Task;
    }

마지막으로 다음 사항도 유용하다는 것을 알았습니다.

나는 항상이 질문을 받는다. 그 의미는 외부 리소스에 대한 I / O 호출을 차단하는 어딘가에 스레드가 있어야한다는 것입니다. 따라서 비동기 코드는 요청 스레드를 비우지 만 시스템의 다른 곳에서는 다른 스레드를 희생해야합니다. 아뇨, 전혀 아닙니다. 비동기 요청이 확장되는 이유를 이해하기 위해 비동기 I / O 호출의 단순화 된 예를 살펴 보겠습니다. 요청이 파일에 기록되어야한다고 가정 해 봅시다. 요청 스레드는 비동기 쓰기 메소드를 호출합니다. WriteAsync는 BCL (Base Class Library)에 의해 구현되며 비동기 I / O에 완료 포트를 사용합니다. 따라서 WriteAsync 호출은 비동기 파일 쓰기로 OS에 전달됩니다. 그런 다음 OS는 드라이버 스택과 통신하여 데이터를 전달하여 I / O 요청 패킷 (IRP)에 씁니다. 이곳은 흥미로운 일입니다. 장치 드라이버가 IRP를 즉시 처리 할 수없는 경우 비동기 적으로 처리해야합니다. 따라서 드라이버는 디스크에 쓰기 시작을 지시하고 OS에 “보류 중”응답을 반환합니다. OS는 해당“보류”응답을 BCL에 전달하고 BCL은 불완전한 작업을 요청 처리 코드로 반환합니다. 요청 처리 코드가 작업을 기다리고 있으며,이 작업은 해당 메소드 등에서 불완전한 작업을 반환합니다. 마지막으로 요청 처리 코드가 완료되지 않은 작업을 ASP.NET으로 반환하고 요청 스레드가 해제되어 스레드 풀로 돌아갑니다. 요청 처리 코드가 작업을 기다리고 있으며,이 작업은 해당 메소드 등에서 불완전한 작업을 반환합니다. 마지막으로 요청 처리 코드가 완료되지 않은 작업을 ASP.NET으로 반환하고 요청 스레드가 해제되어 스레드 풀로 돌아갑니다. 요청 처리 코드가 작업을 기다리고 있으며,이 작업은 해당 메소드 등에서 불완전한 작업을 반환합니다. 마지막으로 요청 처리 코드가 완료되지 않은 작업을 ASP.NET으로 반환하고 요청 스레드가 해제되어 스레드 풀로 돌아갑니다.

ASP.NET의 Async / Await 소개

목표가 응답 성이 아닌 확장 성을 향상시키려는 경우,이를 수행 할 수있는 기회를 제공하는 외부 I / O의 존재에 의존합니다.


답변

메서드를 비동기 적으로 만드는 간단한 방법 중 하나는 Task.Yield () 메서드를 사용하는 것입니다. MSDN은 다음과 같이 말합니다.

await Task.Yield ();를 사용할 수 있습니다. 비동기 메소드에서 강제로 메소드가 비동기 적으로 완료되도록합니다.

메소드의 시작 부분에 삽입하면 호출자에게 즉시 리턴되어 다른 스레드에서 나머지 메소드를 완료합니다.

private async Task<DateTime> CountToAsync(int num = 1000)
{
    await Task.Yield();
    for (int i = 0; i < num; i++)
    {
        Console.WriteLine("#{0}", i);
    }
    return DateTime.Now;
}


답변