将async上传到Azure Blob Storage永远不会返回

时间:2016-06-09 13:44:18

标签: c# azure async-await azure-storage-blobs

我尝试使用异步方法将文件上传到Azure blob存储,然后设置其元数据,但UploadFromByteArrayAsync方法永远不会返回。

我有以下代码:

var connAzureBlob = ConfigurationManager.AppSettings["AzureBlobStorage"];    
var storageAccount = CloudStorageAccount.Parse(connAzureBlob);
var blobClient = storageAccount.CreateCloudBlobClient();

var fileContainer = blobClient.GetContainerReference(ConfigurationManager.AppSettings["AzureBlobContainer"]);
if (!fileContainer.Exists())
{
    await fileContainer.CreateAsync();
    await fileContainer.SetPermissionsAsync(new BlobContainerPermissions {
                                                                             PublicAccess = BlobContainerPublicAccessType.Blob
                                                                         });
}

try
{
    var fileBlob = fileContainer.GetBlockBlobReference(documentId.ToString());
    await fileBlob.UploadFromByteArrayAsync(buffer, 0, buffer.Length);
    Log.Info($"{nameof(SaveToBlobAsync)}: blob {documentId} uploaded.");

    await fileBlob.FetchAttributesAsync();
    fileBlob.Properties.ContentType = contentType;
    fileBlob.Metadata["..."] = "...";
    await fileBlob.SetMetadataAsync();
    Log.Info($"{nameof(SaveToBlobAsync)}: {documentId} - metadata saved.");
}
catch (Exception exception)
{
    Log.Error($"{nameof(SaveToBlobAsync)}: An exception was thrown while saving a file to a blob: ", exception);
    throw;
}

使用上面的代码,我希望看到以下消息记录:

  

SaveToBlobAsync:blob 123已上传   SaveToBlobAsync:123 - 保存元数据。

但这些从未出现过。

但是,blob似乎确实存储了(我可以使用Azure Storage Explorer查看其内容)但是该过程似乎没有移动到下一行(这将是第一个日志消息)

如果我为非异步对应方切换所有异步调用,则代码按预期工作。

任何人都可以解释为什么UploadFromByteArrayAsync似乎无法返回?

更新 我想我会添加一些关于上下文的更多信息,以防它有用。

此代码是从Web API方法调用的。有问题的控制器没有异步代码,除了调用存储库方法之外别无其他。存储库方法在调用与blob存储接口的类之前,再次更新SQL DB - 所有这些都是非异步代码。

这个面向blob存储的类具有非异步方法,除了使用.Wait().Result调用异步等效项之外别无其他功能。

在有问题的情况下,第二阶段的存储库正在调用非异步版本,因此上面的代码基本上是使用.Wait()调用的。

在上面的代码之前,此HTTP请求中没有其他异步调用,从它进入Web API的控制器。

1 个答案:

答案 0 :(得分:7)

错误几乎肯定会进一步上升到调用堆栈,其中某些方法阻止返回的任务(例如Task<T>.ResultTask.Wait等)。如果任务完成需要阻塞线程,这将导致死锁。

I explain this situation in full on my blog,但其要点是:

    默认情况下,
  • await会捕获&#34;上下文&#34;并使用它来恢复async方法。
  • 如果此代码在ASP.NET请求上下文中执行,则此&#34; context&#34;是一个ASP.NET SynchronizationContext;如果此代码在UI线程中执行,则此&#34; context&#34;是一个用户界面SynchronizationContext
  • ASP.NET SynchronizationContext和UI SynchronizationContext一次只允许其中包含一个帖子。

因此,当返回的Task上的调用代码阻止时,它在该上下文中持有一个线程,阻止了Task完成。