我一直在努力与异步/等待一周。一些背景知识:下面的代码是MVC4网站项目的一部分。该网站发生了大量的API调用。目标是使这些API调用并行发生而不是同步,以提高站点响应能力。现在所有API调用都相互阻塞。因此,如果一个页面需要4次调用......加载时间更长。
我为所有API调用的同步和异步版本构建了单独的方法。我遇到的问题是await永远不会响应。我认为这与this question有关。但是,我真的不确定如何解决它。我已经尝试过ConfigureAwait(false),这对我没有帮助。
以下是代码:
控制器中的初始调用看起来如此:
BaseData bdata = API.GetBaseData().Result;
我很乐意在这里使用await,但是如果没有AsyncController这不是一个选项,由于需要请求/响应访问,我们无法使用它。其他方法在API类中:
internal static async Task<BaseData> GetBaseData() {
var catTask = GetParentCategoriesAsync();
var yearTask = GetYearsAsync();
await Task.WhenAll(new Task[] { catTask, yearTask });
var bdata = new BaseData {
years = await yearTask,
cats = await catTask
};
return bdata;
}
internal static async Task<List<APICategory>> GetParentCategoriesAsync() {
try {
WebClient wc = new WebClient();
wc.Proxy = null;
string url = getAPIPath();
url += "GetFullParentCategories";
url += "?dataType=JSON";
Uri targeturi = new Uri(url);
List<APICategory> cats = new List<APICategory>();
var cat_json = await wc.DownloadStringTaskAsync(targeturi);
cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json);
return cats;
} catch (Exception) {
return new List<APICategory>();
}
}
internal static async Task<List<double>> GetYearsAsync() {
WebClient wc = new WebClient();
wc.Proxy = null;
Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON");
var year_json = await wc.DownloadStringTaskAsync(targeturi);
List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json);
return years;
}
当调用这些方法时,我可以在GetYearsAsync()和GetParentCategoriesAsync()中放置断点。一切都会一直触发,直到等待wc.DownloadStringTaskAsync(targeturi)命令。那就是停止。
我已将ConfigureAwait(continueOnCapturedContext:false)添加到所有任务中,但这没有帮助。我假设问题是线程不在同一个上下文中。但是,我不确定。但我确信,我做错了什么。我只是不确定是什么。它或者我只是想尝试做一些用.NET MVC4无法完成的事情。任何想法都会受到极大的赞赏。
答案 0 :(得分:2)
问题实际上是由于WebClient
,始终同步回请求上下文(由于Result
调用而被阻止)。
您可以使用HttpClient
代替ConfigureAwait(false)
:
internal static async Task<BaseData> GetBaseDataAsync() {
var catTask = GetParentCategoriesAsync();
var yearTask = GetYearsAsync();
await Task.WhenAll(catTask, yearTask).ConfigureAwait(false);
var bdata = new BaseData {
years = await yearTask,
cats = await catTask
};
return bdata;
}
internal static async Task<List<APICategory>> GetParentCategoriesAsync() {
try {
var client = new HttpClient();
string url = getAPIPath();
url += "GetFullParentCategories";
url += "?dataType=JSON";
Uri targeturi = new Uri(url);
List<APICategory> cats = new List<APICategory>();
var cat_json = await client.GetStringAsync(targeturi).ConfigureAwait(false);
cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json);
return cats;
} catch (Exception) {
return new List<APICategory>();
}
}
internal static async Task<List<double>> GetYearsAsync() {
var client = new HttpClient();
Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON");
var year_json = await client.GetStringAsync(targeturi).ConfigureAwait(false);
List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json);
return years;
}
这应该让你可以这样称呼它:
BaseData bdata = API.GetBaseDataAsync().Result;
但是,我强烈建议你这样称呼它:
BaseData bdata = await API.GetBaseDataAsync();
您会发现await
之前和之后的代码都可以访问请求和响应上下文。
答案 1 :(得分:0)
我结束了Servy和Stephen Cleary的建议。根据Stephen的回复,我意识到只要我在控制器中进行任何异步调用之前就可以访问请求/响应。
如果我将HttpContext存储在变量中,我可以将该上下文传递给需要访问它的任何模型/服务方法。这让我可以像Servy建议的那样一直保持异步。在那之后,我对任何我想要的异步模式没有任何问题。