Azure downloadtostreamasync method hangs

binderbound picture binderbound · Feb 15, 2015 · Viewed 8.9k times · Source

here is the offending code

    public async static Task<MemoryStream> AsyncReadBlob(string identifier)
    {
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageString);
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference(containerName);
        MemoryStream memstream = new MemoryStream();
        CloudBlockBlob blockBlob = container.GetBlockBlobReference(identifier);
        await blockBlob.DownloadToStreamAsync(memstream);
        return memstream;
    }

Basically, the code runs completely fine if I replace

await blockBlob.DownloadToStreamAsync(memstream)

with

blockblob.DownloadToStream(memstream)

or

blockblob.DownloadToStreamAsync(memstream).wait()

But the moment I make it asynchronous, the "download to stream" step never completes. I've also tried using DownloadToStreamBegin() and then waiting for it to complete too, but it never finishes, once again.

Is it not working because it's a static method? Or is it because it's memstream is being de-referenced from memory or something??

I'm using asp.net 4.5, all compiles properly, no interpreter errors or exceptions thrown.

Any help would be greatly appreciated

Answer

Carlos G. picture Carlos G. · Feb 15, 2015

Short version... Use:

 await blockBlob.DownloadToStreamAsync(memstream).ConfigureAwait(false);

Long version:

When you await a task in C#, the task scheduler will try to execute the block of code that comes after the await in the same thread that called the original await.

The problem is that in some cases (can't recall right now the specific circumstances) the ASP.NET Thread Scheduler will block the thread while waiting for the Task to return. When the task returns, the task blocks waiting for the original thread to be released, while the thread is blocked waiting for the task to end. So you end deadlocked.

ConfigureAwait allows you to deactivate that behavior.