使用异步BCL和异步功能

时间:2014-01-06 14:47:56

标签: c# .net .net-4.0 async-await

我将此标记为.NET 4,因为我正在使用async BCL

我有以下代码:

using System.Threading.Tasks;

public static async Task<ObservableCollection<MyResult>> GetMyData(Params p)
{
    DoStuffClass stuff = new DoStuffClass();
    ObservableCollection<MyResults> results = null;

    await Task.Factory.StartNew(() =>
    {
        results = stuff.LongDrawnOutProcess(p);
    });

    return results;
}

我从之前的版本重构了一下,看起来像这样:

static void GetMyDataAsync(Params p, EventHandler<MyArgs> callback)
{
    DoStuffClass stuff = new DoStuffClass();
    stuff.LoadCompleted += callback;
    stuff.LongDrawOutProcessAsync(p)
}

这方面的灵感来自here

到目前为止,第一种情况的使用是最简单的,这就是我重构的原因;我的问题是:这种方法有什么缺陷吗? LongDrawnOutProcess确实命中了数据库。

1 个答案:

答案 0 :(得分:6)

您应该使用任务的Result来访问Task的结果,而不是依赖设置另一个变量的任务的副作用。虽然在这种情况下你没有过早地访问结果,但在使用这种编程风格时很容易犯错误。您还需要担心是否存在适当的内存屏障,以便正确观察更改后的变量。

更优选的方法是:

public static async Task<ObservableCollection<MyResult>> GetMyData(Params p)
{
    DoStuffClass stuff = new DoStuffClass();

    return await Task.Factory.StartNew(() => stuff.LongDrawnOutProcess(p));
}

接下来,由于除了返回值之外你在await之后再也没有做任何事情,所以没有真正的理由在这里使用async;你可以写:

public static Task<ObservableCollection<MyResult>> GetMyData(Params p)
{
    DoStuffClass stuff = new DoStuffClass();

    return Task.Factory.StartNew(() => stuff.LongDrawnOutProcess(p));
}

最后,这确实有一个更基本的问题。您没有使用实际的异步方法。您正在创建一个线程池线程,它将花费所有时间来等待此方法完成。这是浪费,异步编程的设计是避免这种同步等待,而不是将它们推送到另一个线程。如果你希望你的程序是异步的,你仍然应该使用Async方法,但你仍然可以将它转换为基于任务的异步风格:

static Task<ObservableCollection<MyResult>> GetMyDataAsync(Params p)
{
    var tcs = new TaskCompletionSource<ObservableCollection<MyResult>>();
    DoStuffClass stuff = new DoStuffClass();
    stuff.LoadCompleted += (args) => tcs.TrySetResult(args.Result);
    stuff.LongDrawOutProcessAsync(p);
    return tcs.Task;
}