Инкапсуляция метода синхронизации с асинхронным CTP не работает - PullRequest
2 голосов
/ 13 февраля 2012

В прошлом году я написал библиотеку веб-API с классическими синхронными и асинхронными методами.Сейчас я пытаюсь добавить методы TaskAsync, используя новый C # Async CTP 3 .

Я написал этот простой код для инкапсуляции метода синхронизации:

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 Ответ

4 голосов
/ 13 февраля 2012

Вы создаете задачу, но никогда запускаете ее.Он создан как "холодный" - ему нужно что-то для запуска, прежде чем функция, предоставленная конструктору, будет фактически вызвана.

Либо вызовите Task.Start(), либо используйте TaskEx.Run() или Task.Factory.StartNew() вместо вызова конструктора Task:

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

Обратите внимание, что здесь нет необходимости в анонимном типе - просто позвольте компилятору захватить параметры.

...