Jak utworzyć metodę asynchroniczną w C#?

Każdy wpis na blogu, który przeczytałem, mówi ci, jak korzystać z metody asynchronicznej w C#, ale z jakiegoś dziwnego powodu nigdy nie wyjaśnia, jak zbudować własne metody asynchroniczne do konsumpcji. Więc mam teraz ten kod, który pochłania moją metodę:

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

A ja napisałem tą metodę, która jest 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);
}

Czy to, użycie Task.Factory, jest najlepszym sposobem na napisanie metody asynchronicznej, czy powinienem napisać to w inny sposób?

Author: Peter Mortensen, 2013-04-17

2 answers

Nie polecam StartNew chyba, że potrzebujesz takiego poziomu złożoności.

Jeśli twoja metoda asynchroniczna jest zależna od innych metod asynchronicznych, najprostszym podejściem jest użycie słowa kluczowego 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;
}

Jeśli twoja metoda asynchroniczna wykonuje pracę procesora, powinieneś użyć Task.Run:

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

You may find my async/await intro pomocne.

 185
Author: Stephen Cleary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 15:30:59

Jeśli nie chcesz używać async/wait wewnątrz metody, ale nadal "ozdobić" ją tak, aby móc używać słowa kluczowego wait z zewnątrz, 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; 
}

Stąd i tutaj

Aby wspierać taki paradygmat zadaniami, potrzebujemy sposobu na zachowanie funkcji Zadania i możliwości odwoływania się do arbitralnej operacji asynchronicznej jako zadania, ale do kontrolowania żywotności tego zadania zgodnie z regułami podstawowego infrastruktury, która zapewnia asynchroniczność, i zrobić to w sposób, który nie kosztuje znacząco. To jest cel TaskCompletionSource.

I saw jest również używany w źródle. NET np. 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;
    }

Wreszcie, znalazłem przydatne również następujące:

Zadają mi to pytanie cały czas. Implikacja jest taka, że gdzieś musi być jakiś wątek, który blokuje wywołanie I / O do zewnętrznego zasobu. Kod asynchroniczny zwalnia wątek żądania, ale tylko kosztem innego wątku w innym systemie, prawda? Nie, wcale nie. Aby zrozumieć, dlaczego asynchroniczne żądania skalują się, prześledzę (uproszczony) przykład asynchronicznego wywołania We/Wy. Załóżmy, że żądanie wymaga zapisu do pliku. Wątek żądania wywołuje metodę zapisu asynchronicznego. WriteAsync jest zaimplementowany przez bibliotekę klas bazowych (BCL) i używa portów dopełnienia dla asynchronicznych wejść / wyjść. asynchroniczny zapis pliku. System operacyjny komunikuje się ze stosem sterowników, przekazując dane do zapisu w pakiecie I/O request packet (IRP). W tym miejscu robi się ciekawie: jeśli sterownik urządzenia nie może obsługiwać IRP natychmiast, musi obsługiwać go asynchronicznie. Tak więc sterownik mówi dyskowi, aby rozpoczął zapis i zwraca odpowiedź "oczekującą" do systemu operacyjnego. System operacyjny przekazuje tę" oczekującą " odpowiedź do BCL, a BCL zwraca niekompletne zadanie do kodu obsługi żądań. Kod obsługi żądania oczekuje zadania, które zwraca niekompletne zadanie z tej metody i tak dalej. Wreszcie kod obsługi żądania kończy się zwrotem niekompletnego zadania do ASP.NET, a wątek żądania jest zwolniony, aby powrócić do puli wątków.

Wprowadzenie do Async / ASP.NET

Jeśli celem jest poprawa skalowalności (a nie responsywności), wszystko zależy od istnienia zewnętrznego wejścia/wyjścia, które daje taką możliwość.

 3
Author: Alberto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-03 19:45:13