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

发布于 2025-01-05 01:06:56 字数 1943 浏览 1 评论 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);
        }
    }
}

问题是:我的同步方法永远不会被调用。 调试器转到 < code>result = wait 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;
    }
}

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

Last year, I wrote a web API library with classic synchronous and asynchronous methods. I'm now trying to add TaskAsync methods using the new C# Async CTP 3.

I wrote this simple code to encapsulate the sync method:

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;
    }
}

Then, from my WPF 4 application, I have a async method using it:

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);
        }
    }
}

The issue is: my synchronous method never gets called. The debugger goes to result = await client.AuthenticateTaskAsync(username, password);, the debugger continues its work and never comes back. The breakpoint within the sync Authenticate nevers breaks. UpdateStatus never gets called. Quite strange (I though it was a debugger implementation issue).

Then I looked at how WebClient.DownloadStringTaskAsync is implemented. I changed my API client method to this:

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;
    }
}

And now it works. Can someone explain why the first code doesn't work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

为你鎻心 2025-01-12 01:06:56

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

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

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

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

You're creating the task but never starting it. It's created "cold" - it would need something to start it before the function provided to the constructor is actually called.

Either call Task.Start(), or use TaskEx.Run() or Task.Factory.StartNew() instead of calling the Task constructor:

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

Note that there's no need for the anonymous type here - just let the compiler capture the parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文