Azure 存储表:等待 table.ExecuteAsync(InsertOperation) 执行,但从未完成
Azure Storage Tables: await table.ExecuteAsync(InsertOperation) executes, but never finishes
嗯,我的问题是在 Azure 存储 Table 上调用 await table.ExecuteAsync(...) 确实插入了请求的数据,但从未完成(不 return Table 结果)。 InsertOrUpdate 和 Update 操作的情况相同。我还尝试了具有不同数量属性的不同表 - 同样的问题。
当我调用 table.Execute(...) - 每种操作都运行良好。
这是我的代码 - 就这么简单:
外部调用(在异步操作中放在 MVC Controller 中):
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;
}
这是一个经典的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
问题出在这里:
Task.WaitAll(addPostTasks.ToArray());
您的异步方法尝试将自身编组回 ASP.NET 同步上下文,由于您使用 Task.WaitAll
.
启动了阻塞调用,该同步上下文被卡住了
相反,您需要一直遵循 async 模式并使用 Task.WhenAll
,然后 await
:
await Task.WhenAll(addPostTasks.ToArray);
Stephan Cleary 在他的博客 post(@NedStoyanov 添加)中详细阐述了这一点:
One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article
about SynchronizationContext.
如果您为某个组织工作,问题可能出在您的组织启用的代理设置上。我遇到了类似的问题,为了解决这个问题,我使用了以下步骤:
1:在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:在 appsettings.json
添加
"defaultProxy": {
"proxyaddress": "<your IP here>"
},
嗯,我的问题是在 Azure 存储 Table 上调用 await table.ExecuteAsync(...) 确实插入了请求的数据,但从未完成(不 return Table 结果)。 InsertOrUpdate 和 Update 操作的情况相同。我还尝试了具有不同数量属性的不同表 - 同样的问题。
当我调用 table.Execute(...) - 每种操作都运行良好。
这是我的代码 - 就这么简单:
外部调用(在异步操作中放在 MVC Controller 中):
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;
}
这是一个经典的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
问题出在这里:
Task.WaitAll(addPostTasks.ToArray());
您的异步方法尝试将自身编组回 ASP.NET 同步上下文,由于您使用 Task.WaitAll
.
相反,您需要一直遵循 async 模式并使用 Task.WhenAll
,然后 await
:
await Task.WhenAll(addPostTasks.ToArray);
Stephan Cleary 在他的博客 post(@NedStoyanov 添加)中详细阐述了这一点:
One other important point: an ASP.NET request context is not tied to a specific thread (like the UI context is), but it does only allow one thread in at a time. This interesting aspect is not officially documented anywhere AFAIK, but it is mentioned in my MSDN article about SynchronizationContext.
如果您为某个组织工作,问题可能出在您的组织启用的代理设置上。我遇到了类似的问题,为了解决这个问题,我使用了以下步骤:
1:在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:在 appsettings.json
添加"defaultProxy": {
"proxyaddress": "<your IP here>"
},