如何正确创建一个等待的方法

时间:2017-04-13 13:05:45

标签: c# async-await

我正在写一些等待的方法,我在互联网上找到了很多方法。所以我来到这里是为了了解每一条路上到底发生了什么,以及是否必须放弃某些方法。

据我所知,有两种等待方法:

那些调用其他等待方法的人:

public async Task<Foo> GetFooAsync()
{
    var foo = await TrulyGetFooAsync();

    // Do stuff with foo

    return foo;
}

我没有找到任何其他方法来做到这一点,我认为这是正确的方法。告诉我,如果我错了!

那些只调用非等待方法的人:

这里出现了问题。

例如,我看到了这个:

例1

public async Task<Foo> GetFooAsync()
{
    return await Task.Run(() => TrulyGetFoo());
}

据我了解,async / await关键字是无用的,可以避免这样做:

例2

public Task<Foo> GetFooAsync()
{
    return Task.Run(() => TrulyGetFoo());
}

这最后一个例子是我到目前为止所做的事情。关于那,是否有区别:

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

Task.Run((Foo)TrulyGetFoo); // I don't know if the cast is required at any time but in my code, it was

???

但我最近发现了这种方式:

例3

public Task<Foo> GetFooAsync()
{
    TaskCompletionSource<Foo> tcs = new TaskCompletionSource<Foo>();
    tcs.SetResult(TrulyGetFoo());
    return tcs.Task;
}

如果我理解正确,一个等待的方法并不总是在另一个线程上运行???我的猜测是第三个例子是提供这种机制(但是如何在第三个例子中看到同步代码),当示例1和2总是在工作线程上运行时???

可能还有另外一种方法来编写等待方法,所以让我知道它们。

3 个答案:

答案 0 :(得分:3)

简短版本是这样的:可以在async代码块中等待返回任务的任何内容。

public async Task MyAsyncMethod()
{
    // do some stuff
    await TrulyAsyncFoo();
    // do some other stuff
    return;
}

如果等待异步调用是方法所做的唯一事情,您可以简单地返回任务本身,这将等待&#34;上游&#34;:

public Task MyAsyncMethod()
{
    return TrulyAsyncFoo();
}

至于在异步方法中调用同步(非异步)代码,它没有任何意义。只需将其称为普通代码:

public async Task MyAsyncMethod()
{
    MySyncMethod();
    await TrulyAsyncFoo();
    MyOtherSyncMethod();
}

Task.Run(() => Foo())几乎总是代码味道,你不能做异步/等待。编写异步代码与编写多线程代码不同。 Async只是告诉编译器你需要等待一些网络或IO绑定任务完成的好方法。

总结一下:

  • Await允许您并排编写异步和同步代码
  • 异步应仅用于等待网络或IO绑定任务,而不是计算绑定任务
  • 异步方法应始终返回TaskTask<T>
  • 避免async void
  • 避免在ASP.NET和其他线程应用程序中使用task.Wait()task.Result进行阻止,除非您知道自己在做什么

答案 1 :(得分:2)

  

例如,我看到围绕同步代码的[Task.Run包装器]

This is a bad practice。我有一篇博文解释why Task.Run should not be used as a method implementation

  

据我了解,async / await关键字是无用的,可以避免

是的,但是I don't recommend eliding async/await in more complex methods

  

我最近找到[TaskCompletionSource<T>]

如评论中所述,您的代码示例仍然是同步的。

要使其异步,您的代码应启动某些操作并返回TaskCompletionSource<T>.Task。然后,只要该操作完成,您的完成处理程序应该调用TaskCompletionSource<T>.SetResult(或类似的方法)。有关示例,请参阅TAP wrappers for EAPTAP wrappers for WaitHandles

TaskFactory.FromAsync也是TaskCompletionSource<T>的封套,用于TAP wrappers for APM

答案 2 :(得分:-3)

我将此用于操作我的数据库的异步事件

private async void btnSave_Click(object sender, EventArgs e)
    {

             success = await someClass.someMethod(some args);

    }

和someMethod就是这样的任务:

public static async Task<someObject> someMethod(args)
    {
       //do something here
    }