13

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

binderbound
  • 755
  • 6
  • 24
  • using the `await` keyword means that the main thread will be freed up to allow other things to be processed, the execution of the function won't continue until it has done so. So you should be calling `await` on the method `DownloadToStreamAsync`! The state machine will remember where the current execution has got to, i.e. that `await` block, then return to it once the asynchronous method has finished. – Callum Linington Feb 15 '15 at 13:29
  • I am using await on DownloadToStreamAsync ?? – binderbound Feb 15 '15 at 14:04
  • You should 3 examples of the ways you tried to execute the same function, and what I was saying is why you should use `await`, I'm only trying to provide some more understanding on the subject – Callum Linington Feb 16 '15 at 08:46

2 Answers2

20

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.

Carlos G.
  • 4,313
  • 2
  • 34
  • 57
  • Cool. That seemed to clear the problem right up. I'm not going to be returning a different memorystream because I'm using a different thread for the remaining code I hope? :/ – binderbound Feb 15 '15 at 14:08
  • For people who read in future - here is an article further explaining why deadlock occurs: http://code.jonwagner.com/2012/09/04/deadlock-asyncawait-is-not-task-wait/ – binderbound Feb 15 '15 at 14:26
  • 1
    @binderbound, link broken :'( – ono2012 Aug 27 '19 at 22:56
9

Your code almost certainly is calling Task<T>.Result or Task.Wait further up the call stack from AsyncReadBlob, and it is this blocking code that is causing the deadlock. I describe this scenario in more detail on my blog.

The correct solution is to replace all Result and Wait calls with await.

Stephen Cleary
  • 376,315
  • 69
  • 600
  • 728
  • 2
    True. The caller of asyncreadblob used await, caller of that used await, caller of that used await, however one caller of that uses .Result I appreciate that your answer is probably more correct in the general scenario, except that I DO want to run this async method syncronously, so... configureAwait is more useful for me. I am going to redesign so I'm not calling this method from Masterpage (masterpage doesn't like async methods), and then I'll use your solution. – binderbound Feb 15 '15 at 23:51