使用异步CTP封装同步方法不起作用

时间:2012-02-13 13:14:32

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

去年,我使用经典的同步和异步方法编写了一个Web API库。我现在正尝试使用新的 C#Async CTP 3 添加TaskAsync方法。

我编写了这个简单的代码来封装同步方法:

partial class Client : IClient {
    public string Authenticate(string username, string password)
    {
        // long synchronous code here
    }
    public Task<string> AuthenticateTaskAsync(string username, string password) {
        var p = new { username = username, password = password };
        var task = new Task<string>(p1 => this.Authenticate(p.username, p.password), p);
        return task;
    }
}

然后,从我的WPF 4应用程序中,我有一个使用它的异步方法:

public class LoginViewModel {
    private IClient client;

    // called by an ICommand
    private async Task DoLogin(string username, string password) {
        UpdateStatus("Authenticating...", true);
        try {
            var result = await client.AuthenticateTaskAsync(username, password);
            // do stuff with result
            UpdateStatus("Authenticated. Fetching User informations...", true);
        } catch (Exception ex) {
            UpdateStatus("Authentication error.", ex, false);
        }
    }
}

问题是:我的同步方法永远不会被调用。调试器转到result = await client.AuthenticateTaskAsync(username, password);,调试器继续工作,永远不会回来。同步Authenticate内的断点断开。 UpdateStatus永远不会被调用。很奇怪(我虽然这是一个调试器实现问题)。

然后我看了WebClient.DownloadStringTaskAsync是如何实现的。我将我的API客户端方法更改为:

partial class Client : IClient {
    public Task<string> AuthenticateTaskAsync(string username, string password) {
        var tcs = new TaskCompletionSource<string>();

        try {
            tcs.TrySetResult(this.Authenticate(username, password));
        } catch (Exception ex) {
            tcs.TrySetException(ex);
        }

        return tcs.Task;
    }
}

现在它有效。有人可以解释为什么第一个代码不起作用吗?

1 个答案:

答案 0 :(得分:4)

您正在创建任务但从未启动它。它被创建为“冷” - 在实际调用提供给构造函数的函数之前需要启动它。

请致电Task.Start(),或使用TaskEx.Run()Task.Factory.StartNew()代替调用Task构造函数:

public Task<string> AuthenticateTaskAsync(string username, string password) {
    return TaskEx.Run(() => this.Authenticate(username, password));
}

请注意,此处不需要匿名类型 - 只需让编译器捕获参数。