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?
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.