Подтвердить что ты не робот

Как написать "ожидаемый" метод?

Наконец-то я изучаю async и жду ключевых слов, которые я как бы "получаю", но все примеры, которые я видел, вызывают методы async в .Net-среде, например. этот, который вызывает HttpClient.GetStringAsync().

То, что я не так понимаю, - это то, что происходит в таком методе, и как я напишу свой собственный "ожидаемый" метод. Это так же просто, как обертывание кода, который я хочу запустить асинхронно в задаче, и вернуть это?

4b9b3361

Ответ 1

Это так же просто, как

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

Чтобы сделать его ожидаемым методом:

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

Важное значение здесь - вернуть задачу. Этот метод даже не должен быть помечен как асинхронный. (Просто прочитайте еще немного, чтобы он появился на картинке)

Теперь это можно назвать

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

Обратите внимание, что здесь подпись метода говорит async, так как метод может вернуть управление вызывающему абоненту до тех пор, пока ExpensiveTaskAsync() не вернется. Кроме того, дорогостоящий в этом случае означает трудоемкий процесс, например, веб-запрос или аналогичный. Для отправки тяжелых вычислений в другой поток обычно лучше использовать "старые" подходы, т.е. System.ComponentModel.BackgroundWorker для графических приложений или System.Threading.Thread.

Ответ 2

Как бы я написал свой "ожидаемый" метод? Это так же просто, как обертывание кода, который я хочу запустить асинхронно в Task, и возвратить его?

Это один из вариантов, но скорее всего это не то, что вы хотите сделать, потому что это на самом деле не дает вам многих преимуществ асинхронного кода. Для получения дополнительной информации см. Stephen Toub Должен ли я подвергать асинхронные обертки для синхронных методов?

В общем, методы не ожидаются, типы есть. Если вы хотите написать что-то вроде await MyMethod(), тогда MyMethod() должен вернуть Task, Task<T> или пользовательский тип await. Использование настраиваемого типа - редкий и расширенный сценарий; используя Task, у вас есть несколько вариантов:

  • Напишите свой метод с помощью async и await. Это полезно для компоновки действий асинхронно, но не может использоваться для внутренних вызовов await.
  • Создайте Task с помощью одного из методов на Task, например Task.Run() или Task.FromAsync().
  • Используйте TaskCompletionSource. Это самый общий подход, его можно использовать для создания await способных методов из всего, что произойдет в будущем.

Ответ 3

... как бы я написал свой собственный "ожидаемый" метод.

Возврат Task - не единственный способ. У вас есть возможность создать настраиваемого ожидающего (путем реализации GetAwaiter и INotifyCompletion), вот отличное чтение: "Ожидайте что угодно". Примеры API.NET, возвращающих пользовательские ожидающие: 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();

Ответ 4

Да, технически вам нужно вернуть Task или Task<Result> из метода async для реализации ожидаемого метода.

Это поддерживает Асинхронный шаблон на основе задач.

Однако существует несколько способов реализации TAP. Подробнее см. Реализация Асинхронного шаблона на основе задач.

(Но все эти реализации все равно возвращают Task или Task<Result>, конечно.)

Ответ 5

Просто преобразуйте свой метод в задачу. как @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
}

Теперь скажем, что у вас

void foo1()

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

Ответ 6

Если вы не хотите использовать Task, вы можете написать полностью настроенный ожидаемый объект. Такой объект - это объект, реализующий метод GetAwaiter() возвращающий объект, реализующий INotifyCompletion, которым может быть сам объект.

Подробнее: INotifyCompletion

Официант реализует:

  • IsCompleted это получить состояние
  • GetResult() чтобы получить результат
  • OnCompleted (Action continuation) для установки продолжения делегата.

Ожидаемый объект содержит некоторый метод для фактической полезной нагрузки (например, ниже, метод 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 ();
    }
}