我有以下方法:
public string RetrieveHolidayDatesFromSource() {
var result = this.RetrieveHolidayDatesFromSourceAsync();
/** Do stuff **/
var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/
return returnedResult;
}
private async Task<string> RetrieveHolidayDatesFromSourceAsync() {
using (var httpClient = new HttpClient()) {
var json = await httpClient.GetStringAsync(SourceURI);
return json;
}
}
以上操作不起作用,似乎没有正确返回任何结果。我不知道我在哪里错过强制等待结果的陈述?我希望RetrieveHolidayDatesFromSource()方法返回一个字符串。
以下工作正常,但它是同步的,我相信它可以改进吗?请注意,下面是同步的,我想将其更改为异步但由于某种原因无法解决问题。
public string RetrieveHolidayDatesFromSource() {
var result = this.RetrieveHolidayDatesFromSourceAsync();
/** Do Stuff **/
var returnedResult = this.TransformResults(result); /** This is where Result is actually used**/
return returnedResult;
}
private string RetrieveHolidayDatesFromSourceAsync() {
using (var httpClient = new HttpClient()) {
var json = httpClient.GetStringAsync(SourceURI);
return json.Result;
}
}
我错过了什么吗?
注意:出于某种原因,当我断开上述异步方法时,当它到达线路时#var; var json = await httpClient.GetStringAsync(SourceURI)&#34;它只是走出了断点,我无法回到方法中。
答案 0 :(得分:23)
我错过了什么吗?
是。异步代码 - 本质上 - 暗示当操作正在进行时不使用当前线程。同步代码 - 就其本质而言 - 意味着在操作进行时当前线程被阻塞。这就是为什么从同步代码中调用异步代码甚至没有意义。事实上,正如我在博客中描述的那样,a naive approach (using Result
/Wait
) can easily result in deadlocks。
首先要考虑的是:应该我的API是同步还是异步?如果它处理I / O(如本例所示),则为should be asynchronous。所以,这将是一个更合适的设计:
public async Task<string> RetrieveHolidayDatesFromSourceAsync() {
var result = await this.DoRetrieveHolidayDatesFromSourceAsync();
/** Do stuff **/
var returnedResult = this.TransformResults(result); /** Where result gets used **/
return returnedResult;
}
正如我在async best practices article中所描述的那样,你应该“一直”异步“。如果你不这样做,你无论如何都不会从异步中获得任何好处,所以为什么要这么麻烦?
但是,假设您对最终异步感兴趣,但是现在您无法更改所有内容,您只想更改 part < / em>你的应用。这是一种非常普遍的情况。
在这种情况下,正确的方法是公开两个同步和异步API。最终,在升级所有其他代码之后,可以删除同步API。我在article on brownfield async development中探索了这种场景的各种选项;我个人最喜欢的是“bool参数hack”,它看起来像这样:
public string RetrieveHolidayDatesFromSource() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult();
}
public Task<string> RetrieveHolidayDatesFromSourceAsync() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false);
}
private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {
var result = await this.GetHolidayDatesAsync(sync);
/** Do stuff **/
var returnedResult = this.TransformResults(result);
return returnedResult;
}
private async Task<string> GetHolidayDatesAsync(bool sync) {
using (var client = new WebClient()) {
return sync
? client.DownloadString(SourceURI)
: await client.DownloadStringTaskAsync(SourceURI);
}
}
这种方法避免了代码重复,并避免了与其他“同步异步”反模式解决方案相同的死锁或重入问题。
请注意,我仍然会将生成的代码视为正确异步API路径上的“中间步骤”。特别是,内部代码必须回退到WebClient
(支持同步和异步)而不是首选HttpClient
(仅支持异步)。一旦所有调用代码都更改为使用RetrieveHolidayDatesFromSourceAsync
而不是RetrieveHolidayDatesFromSource
,那么我将重新访问并删除所有技术债务,将其更改为使用HttpClient
并仅为异步。
答案 1 :(得分:3)
public string RetrieveHolidayDatesFromSource() {
var result = this.RetrieveHolidayDatesFromSourceAsync().Result;
/** Do stuff **/
var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/
return returnedResult;
}
如果您将 .Result 添加到异步调用,它将执行并等待结果到达,强制它是同步的
更新:
private static string stringTest()
{
return getStringAsync().Result;
}
private static async Task<string> getStringAsync()
{
return await Task.FromResult<string>("Hello");
}
static void Main(string[] args)
{
Console.WriteLine(stringTest());
}
解决评论:这没有任何问题。