【问题标题】:Azure Storage Tables: await table.ExecuteAsync(InsertOperation) executes, but never finishesAzure 存储表:await table.ExecuteAsync(InsertOperation) 执行,但从未完成
【发布时间】:2015-01-28 10:20:24
【问题描述】:

好吧,我的问题是在 Azure 存储表上调用 await table.ExecuteAsync(...) 确实会插入请求的数据,但永远不会完成(不返回 TableResult)。与 InsertOrUpdate 和 Update 操作相同。我还尝试了具有不同属性数量的不同表 - 同样的问题。

当我调用 table.Execute(...) - 每种操作都可以正常工作。

这是我的代码 - 就这么简单:

外部调用(它在异步操作中放置在 MVC 控制器中):

List<Task<ServiceResult<Boolean?>>> addPostTasks = new List<Task<Common.ServiceResult<bool?>>>();              
foreach (var userStream in userStreams)
{
    Task<ServiceResult<Boolean?>> addPostTask = postsStorageSvc.AddImagePost(...);
    postsAddImagePostTasks.Add(addPostTask);
}
Task.WaitAll(addPostTasks.ToArray());

方法调用:

public async Task<ServiceResult<Boolean?>> AddImagePost(...)
{
ServiceResult<Boolean?> result = new ServiceResult<bool?>(null);
try
{
    PostTableEntity newPost = new PostTableEntity(streamId.ToString(), Guid.NewGuid().ToString(), creatorId, date, htmlText);              
    TableOperation insertOperation = TableOperation.Insert(newPost);
    //Following line never ends!
    TableResult tableResult = await this._storageTableBootstrapper.Table.ExecuteAsync(insertOperation);                
    //Following line works perfect - but is not ASYNC
    TableResult tableResult = this._storageTableBootstrapper.Table.Execute(insertOperation);
}
catch (Exception ex)
{
    result.Result = false;
    result.Errors.Add("AzurePostsStorageService Unexpected error: " + ex.Message);
}
return result;
}

【问题讨论】:

  • 你是怎么调用这个方法的?
  • 什么意思?此代码块采用异步方法,我正在更新我的问题以显示周围的行。
  • 显示方法签名以及如何调用它。您是否使用Task.Result 或Task.Wait 阻止它?
  • 我希望现在更清楚我如何称呼它。

标签: c# .net azure async-await azure-table-storage


【解决方案1】:

问题出在这里:

Task.WaitAll(addPostTasks.ToArray());

您的 async 方法尝试将自己编组回 ASP.NET 同步上下文,因为您使用 Task.WaitAll 发起了阻塞调用,所以该上下文被卡住了。

相反,您需要遵循 async all way 模式并使用Task.WhenAll 和await:

await Task.WhenAll(addPostTasks.ToArray);

Stephan Cleary 在他的博文(@NedStoyanov 添加)中详细阐述了这一点:

还有一点很重要:ASP.NET 请求上下文不绑定到 特定线程(就像 UI 上下文一样),但它只允许一个 一次插入。 这个有趣的方面不是官方的 记录在任何地方 AFAIK,但在我的 MSDN 文章中提到 关于 SynchronizationContext。

【讨论】:

    【解决方案2】:

    这是由这行引起的经典deadlock:

    Task.WaitAll(addPostTasks.ToArray());
    

    尝试将其更改为:

    await Task.WhenAll(addPostTasks.ToArray());
    

    基本上Task.WaitAll阻塞了请求线程,它无法执行await table.ExecuteAsync(...)发起的Tasks的延续。另一种选择是在您的内部任务中使用ConfigureAwait(false),以避免切换SynchronizatonContext。

    await table.ExecuteAsync(...).ConfigureAwait(false);
    

    只要您不需要切换到原来的SynchronizationContext,就可以使用ConfigureAwait(false)。在您的情况下,我相信您可以在服务器上的所有等待中做到这一点,await 之后的代码是否在线程池上执行并不重要。

    有关详细信息,请参阅本文:msdn.microsoft.com/enus/magazine/jj991977.aspx

    【讨论】:

    • 您的答案与上述含义相同,但您首先 Yuval Itzchakov 提供了更多信息。
    • 好的,所以要清楚并更好地理解案例。 您是否建议在我们所有位于执行堆栈顶部的异步操作上使用“.ConfigureAwait(false)”(例如 table.ExecuteAsync(...) 甚至我们自己的异步操作/方法)?
    • 除非我在 GUI 线程中并且在 await 之后使用访问 GUI 元素,否则我会这样做。更多详情请看这篇文章:msdn.microsoft.com/en-us/magazine/jj991977.aspx
    【解决方案3】:

    如果您为某个组织工作,问题可能出在您的组织启用的代理设置上。我有一个类似的问题,为了解决这个问题,我使用了以下步骤:

    1:在Configure at startup.cs中添加如下代码

    app.UseDeveloperExceptionPage();
                    var webProxy = new WebProxy(new Uri(Configuration["defaultProxy:proxyaddress"]), BypassOnLocal: false);
                    var proxyHttpClientHandler = new HttpClientHandler
                    {
                        Proxy = webProxy,
                        UseProxy = true,
                    };
                    AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
    

    2:添加 at appsettings.json

    "defaultProxy": {
        "proxyaddress": "<your IP here>"
      },
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-09
      • 2018-01-25
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-20
      • 1970-01-01
      相关资源
      最近更新 更多