如何使用async/await处理同步方法/任务

6

我正在尝试理解如何使用.NET 4.5的async/await关键字来处理本质上是同步的任务。例如,某种复杂的数学计算。在下面的示例中,我使用了Thread.Sleep来模拟这个操作。我的问题是,是否有一种方法可以使这样的方法像异步方法一样运行?如果没有,你是否只需要像ThisWillRunAsyncTest方法中所做的那样,在该同步方法上执行类似于Task.Factory.StartNew的操作。有没有更简洁的方法?

using System.Threading;
using System.Collections.Generic;
using System.Threading.Tasks;

using NUnit.Framework;

[TestFixture]
public class AsyncAwaitTest
{
    [Test]
    //This test will take 1 second to run because it runs asynchronously
    //Is there a better way to start up a synchronous task and have it run in parallel.
    public async void ThisWillRunAsyncTest()
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 5; i++)
        {
            tasks.Add(Task.Factory.StartNew(() => this.RunTask()));
        }

        await Task.WhenAll(tasks);
    }

    [Test]
    //This test will take 5 seconds to run because it runs synchronously.
    //If the Run Task had an await in it, this this would run synchronously.  
    public async void ThisWillRunSyncTest()
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 5; i++)
        {
            tasks.Add(this.RunTask());
        }

        await Task.WhenAll(tasks);
    }

    //This is just an example of some synchronous task that I want to run in parallel.
    //Is there something I can do in this method that makes the async keyword work?  I.e. this would run asynchronously when called from ThisWillRunSyncTest
    public async Task RunTask()
    {
        Thread.Sleep(1000);
    }
}

1
如果RunTask是同步的,那么它根本不应该返回一个Task,而将其转换为任务应该由调用者完成。 - Lee
你可能想阅读这篇博客文章:http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx - John
1个回答

15
一般来说,如果您有需要执行的 并行 工作,应该使用 Parallel 或并行 LINQ。
有时候将 CPU 密集型工作视为异步(即在后台线程上运行)会很方便。这就是 Task.Run 的用途(避免使用 StartNew,如我在我的博客中所述)。
同步方法应该具有同步方法签名:
public void RunTask()
{
  Thread.Sleep(1000);
}

只有在调用代码需要时(即它是 UI 组件,例如视图模型的一部分)才应该使用 Task.Run 进行包装:

var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
  tasks.Add(Task.Run(() => this.RunTask()));
}
await Task.WhenAll(tasks);

这里的原则是在调用中使用Task.Run,而不是在实现中使用;我在我的博客中详细介绍了更多细节。

请注意,如果您有任何真正的复杂性,应该使用Parallel或并行LINQ,而不是一组Task.Run任务。 Task.Run适用于小型操作,但它没有平行类型所具有的所有智能功能。因此,如果这是库的一部分(并且不一定在UI线程上运行),那么我建议使用Parallel

Parallel.For(0, 5, _ => this.RunTask());

值得注意的是,异步单元测试方法应该是async Task,而不是async void。 NUnit v3已经不支持async void单元测试方法。


非常有帮助。感谢提供信息。 - cgotberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接