5

我已经为 async / await 苦苦挣扎了一周。一些背景知识:下面的代码是 MVC4 网站项目的一部分。该网站发生了大量的 API 调用。目标是让这些 API 调用并行而不是同步发生,以提高站点响应能力。现在所有的 API 调用都互相阻塞。因此,如果一页需要 4 次调用...更长的加载时间。

我已经为所有 API 调用的同步和异步版本构建了单独的方法。我遇到的问题是等待从不响应。我认为这与这个问题有关。但是,我真的不确定如何解决它。我已经尝试过 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() 中放置断点。一切都会触发,直到 await wc.DownloadStringTaskAsync(targeturi) 命令。那是停止的地方。

我已将 ConfigureAwait(continueOnCapturedContext: false) 添加到所有任务中,但这并没有帮助。我假设问题是线程不在同一个上下文中。但是,我不确定。但是,我确信我做错了什么。我只是不确定是什么。要么就是这样,要么我只是在尝试做一些 .NET MVC4 无法完成的事情。任何想法将不胜感激。

4

2 回答 2

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可以访问请求和响应上下文。

于 2013-08-02T12:26:25.397 回答
0

我最终采纳了 Servy 和 Stephen Cleary 的建议。根据 Stephen 的响应,我意识到只要在控制器中进行任何异步调用之前就可以访问请求/响应。

如果我将 HttpContext 存储在一个变量中,我可以将该上下文传递给任何需要访问它的模型/服务方法。这让我可以像 Servy 建议的那样一路异步。在那之后,我对异步模式想要的任何东西都没有任何问题。

于 2013-08-07T13:59:35.973 回答