2016-06-09 58 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