Well, my problem is that calling await table.ExecuteAsync(...) on Azure Storage Table does insert the requested data, but never finishes (does not return TableResult). Same case with InsertOrUpdate and Update operations. I also tried different tables with different number of properties - same problem.
When I call table.Execute(...) - everything works fine for every kind of operation.
Here's my code - simple as that:
Outer call (it is placed in MVC Controller in async action):
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());
Method called:
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;
}
The problem is here:
Task.WaitAll(addPostTasks.ToArray());
Your async method tries to marshal itself back to the ASP.NET synchronization context, which is stuck because you initiated a blocking call using Task.WaitAll
.
Instead, you'll need to follow the async all the way pattern and use Task.WhenAll
, and await
on that:
await Task.WhenAll(addPostTasks.ToArray);
Stephan Cleary elaborates on this in his blog post (which @NedStoyanov added):
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.
This is a classic deadlock caused by this line:
Task.WaitAll(addPostTasks.ToArray());
try changing it to:
await Task.WhenAll(addPostTasks.ToArray());
Basically the the Task.WaitAll
blocks the request thread and it is unable to execute the continuation of the Tasks
initiated by await table.ExecuteAsync(...)
. Another alternative is to use ConfigureAwait(false)
on your internal tasks to avoid switching SynchronizatonContext
.
await table.ExecuteAsync(...).ConfigureAwait(false);
You can use ConfigureAwait(false)
whenever you do not require to switch to the original SynchronizationContext
. In your case I believe you can do that with all awaits as you are on the server and it shouldn't matter if the code after await
executes on the thread pool or not.
See this article for more details: msdn.microsoft.com/enus/magazine/jj991977.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With