Reputation: 6878
Is there a recommended best practices/guidance for exposing synchronous and asynchronous versions of an API in a class library? For instance, if I have the following 2 methods defined in a class library:
public Task<string> GetSomeDataAsync()
{
//typically an IO operation that would be awaited on, simplified to return a Task for illustration
return Task<string>.Factory.StartNew( () => "foo");
}
public string GetSomeDataSync()
{
var task = GetSomeDataAsync();
task.ConfigureAwait(continueOnCapturedContext: false);
return task.Result;
}
The above could be hosted in a Winform/WPF/Console/ASP.NET client app. Is it safe for a client to use the synchronous version above which uses task.Result, given that the task is configured not to capture any synchronization context to avoid potential deadlocks
Upvotes: 4
Views: 1158
Reputation: 244998
I think the best advice on this comes from two articles by Stephen Toub:
The second one is more relevant for you. In short, it says that you shouldn't do this. If the caller of your method decides to wait synchronously, he still can make that choice and he is in a better position than you to do it (because he knows the environment, so he should know if deadlocks are an issue, for example).
The above could be hosted in a Winform/WPF/Console/ASP.NET client app. Is it safe for a client to use the synchronous version above which uses task.Result, given that the task is configured not to capture any synchronization context to avoid potential deadlocks
You're wrong about that. ConfigureAwait()
doesn't modify the Task
in any way (especially since the Task
is already executing at that point). All it does is to return a ConfiguredTaskAwaitable
and if you await
that, it won't resume on the captured context.
This means your method would still deadlock.
Upvotes: 5
Reputation: 34912
There are 3 "major" options for async patterns which are endorsed by MS. I'd recommend that you pick one of the patterns and adhere to it... It looks like you're already headed towards the "Task-based Asynchronous Pattern" so you'd have very few mods in order to conform to the pattern. This allows you to create your methods in a way that will be uniform and intuitive to other consumers... They'll know the pattern from the .NET libraries that MS has built. See this for details:
Overview: http://msdn.microsoft.com/en-us/library/jj152938.aspx
Task Async Pattern: http://msdn.microsoft.com/en-us/library/hh873175.aspx
An example of your methods using the Task Asynchronous Pattern with support for cancellation tokens:
public Task<string> GetSomeDataAsync(CancellationToken cancellationToken = null)
{
return Task<string>.Factory.StartNew(() => "foo", cancellationToken);
}
// NOTE: no need to append Sync to the method name, redundant.
public string GetSomeData()
{
var task = GetSomeDataAsync();
task.ConfigureAwait(continueOnCapturedContext: false);
return task.Result;
}
Upvotes: -1