【问题标题】:Encapsulating a sync method with async CTP doesn't work用异步 CTP 封装同步方法不起作用
【发布时间】:2012-03-04 21:27:11
【问题描述】:

去年,我用经典的同步和异步方法编写了一个 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;
    }
}

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

【问题讨论】:

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


    【解决方案1】:

    您正在创建任务,但从未启动它。它是“冷”创建的 - 在实际调用提供给构造函数的函数之前,它需要一些东西来启动它。

    要么调用Task.Start(),要么使用TaskEx.Run()Task.Factory.StartNew() 而不是调用Task 构造函数:

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

    请注意,这里不需要匿名类型 - 只需让编译器捕获参数即可。

    【讨论】:

    • 哦...是的,我非常失败。谢谢你:)
    猜你喜欢
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多