2016-06-09 119 views
3

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

我有以下代码:

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:团块123上传。
SaveToBlobAsync:123 - 保存元数据。

但是这些从不出现。

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

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

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

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

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

该blob面向存储的类具有非异步方法,除了调用与.Wait().Result之间的异步等效外,其他方法几乎没有任何其他功能。

在这种情况下,第二阶段的存储库正在调用非异步版本,因此上述代码实质上是使用.Wait()调用的。

直到上面的代码,这个HTTP请求中没有其他的异步调用,从它进入Web API控制器的地方。

回答

7

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

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

  • await默认情况下,将捕获“上下文”,并用它来恢复async方法。
  • 如果此代码在ASP.NET请求上下文中执行,则此“上下文”是一个ASP.NET SynchronizationContext;如果此代码在UI线程中执行,则此“上下文”为UI SynchronizationContext
  • ASP.NET SynchronizationContext和UI SynchronizationContext一次只允许一个线程在其中。

因此,当调用代码对返回Task,它是保持一个线程上下文中,防止从Task完成。

+0

谢谢你的帮助,斯蒂芬,虽然我不知道这个阻塞可能发生在哪里。我已经向OP中添加了更多信息,以便让情况更加清晰。 – awj

+0

@awj:你说过“在这种情况下,第二阶段的存储库正在调用非异步版本,所以上述代码基本上是使用.Wait()调用的。” - 这是阻塞发生的地方。 –

+0

你可能会解释_why_我不能用'.Wait()'调用我的异步方法吗?我在其他地方没有问题地使用它,那么为什么它会成为一个问题呢? – awj

相关问题