How to write an “awaitable” method?

匿名 (未验证) 提交于 2019-12-03 01:12:01

问题:

I'm finally looking into the async & await keywords, which I kind of "get", but all the examples I've seen call async methods in the .Net framework, e.g. this one, which calls HttpClient.GetStringAsync().

What I'm not so clear on is what goes on in such a method, and how I would write my own "awaitable" method. Is it as simple as wrapping the code that I want to run asynchronously in a Task and returning that?

回答1:

It's as simple as

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

To make it an awaitable method:

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

The important thing here is to return a task. The method doesn't even have to be marked async. (Just read a little bit further for it to come into the picture)

Now this can be called as

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

Note that here the method signature says async, since the method may return control to the caller until ExpensiveTaskAsync() returns. Also, expensive in this case means time-consuming, like a web request or similar. To send off heavy computation to another thread, it is usually better to use the "old" approaches, i.e. System.ComponentModel.BackgroundWorker for GUI applications or System.Threading.Thread.



回答2:

How I would write my own "awaitable" method? Is it as simple as wrapping the code that I want to run asynchronously in a Task and returning that?

That is one option, but it's most likely not what you want to do, because it doesn't actually give you many of the advantages of asynchronous code. For more details, see Stephen Toub's Should I expose asynchronous wrappers for synchronous methods?

In general, methods are not awaitable, types are. If you want to be able to write something like await MyMethod(), then MyMethod() has to return Task, Task or a custom awaitable type. Using a custom type is a rare and advanced scenario; using Task, you have several options:

  • Write your method using async and await. This is useful for composing actions asynchronously, but it can't be used for the inner-most awaitable calls.
  • Create the Task using one of the methods on Task, like Task.Run() or Task.FromAsync().
  • Use TaskCompletionSource. This is the most general approach, it can be used to create awaitable methods from anything that will happen in the future.


回答3:

... how I would write my own "awaitable" method.

Returning a Task is not the only way. You have an option to create a custom awaiter (by implementing GetAwaiter and INotifyCompletion), here is a great read: "Await anything". Examples of .NET APIs returning custom awaiters: Task.Yield(), Dispatcher.InvokeAsync.

I have some posts with custom awaiters here and here, e.g:

// 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:

Yes, technically you only need to return a Task or Task from an async method to implement an awaitable method.

This supports the Task-Based Asynchronous Pattern.

There are several ways of implementing the TAP, however. See Implementing the Task-based Asynchronous Pattern for details.

(But all these implementations still return Task or Task, of course.)



回答5:

Just convert your method to Task. like @Romiox I Usually use this extention

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

Now let we say you have

void foo1()

void foo2(int i1)

int foo3()

int foo4(int i1)

... Then you can declare your [async Method] like @Romiox

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

OR

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

...

Now you van use async and await for any of fooAsync e.g. foo4Async

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!