1

Is it possible to simulate the behaviour of async/await in .NET 4.0 without using the Microsoft.Bcl.Async Package?

I tried running

Task myTask = Task.Factory.Startnew(MyMethod,...)
myTask.ContinueWith((x)=>{
    //do stuff when myTask is completed
},...);
//code that i hoped would run without waiting for myTask

but this blocks my UI while myTask is running. When searching for this issue, i found this question which seems to ask the exact same thing and even presents a solution with a full code sample. However when i tried calling GetResponseWithRetryAsync (or running it with task.Start(), or wrapping it in Task.Factory.StartNew()) it still blocks my UI thread. Why are all these ways of running a task blocking my UI?

edit: sample code which blocks my UI as requested by user1

Task myTask = Task.Factory.StartNew(MyMethod, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
myTask.ContinueWith((x) =>
    { 
     this.Title="Done!";
    }, new CancellationToken(), TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

where MyMethod is

    public void MyMethod(){
        WebRequest request = WebRequest.Create("http://google.com/");
        request.Credentials = CredentialCache.DefaultCredentials;
        WebResponse response = request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);

        string responseFromServer = reader.ReadToEnd();
        Console.WriteLine(responseFromServer);
        reader.Close();
        response.Close();
    }
Community
  • 1
  • 1
H W
  • 2,556
  • 3
  • 21
  • 45
  • If you post a specific minimal example we can tell you exactly if it blocks or not. Otherwise it's just a guess. – i3arnon Feb 24 '15 at 09:40
  • Can you show us the full context of which this method executes? Do you pass to it any `TaskScheduler`? we need more code. – Yuval Itzchakov Feb 24 '15 at 09:57
  • like you see in my edit, i use `TaskScheduler.FromCurrentSynchronizationContext()`. I thought of this as the default setting - does this mean that my performing task is run in the current sync context as well?! – H W Feb 24 '15 at 10:23
  • 1
    Using TaskScheduler.Default does not block my UI... I guess this was a stupid question after all ^^ Thank you for your help! – H W Feb 24 '15 at 10:27
  • @HW Take out `, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());` from your task. you are running your task on the current UI thread by putting that code there.. Hence you block the UI – JKennedy Feb 24 '15 at 10:31

2 Answers2

3

It is possible to imitate async-await without the compiler and framework's help, although extremely difficult to get right. You need to take care of capturing context, exception handling, returning a result and so forth.

The cases you mentioned aren't supposed to block the UI throughout the asynchronous operation. However there may be some synchronous work being done before the asynchronous operation like DNS resolution. If that's the case, use Task.Run and offload that work to a ThreadPool thread to improve responsiveness.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • This sounds like you are saying that `Task.Run` already starts a task asynchronously and would not block my UI Thread? From what i tried and read thus far, this is not true? – H W Feb 24 '15 at 09:47
  • @HW `Task.Run` offloads CPU bound work to another thread. `async` methods don't need a thread throughout their asynchronous parts. `Task.Run` doesn't block. If you have a case where it does then you have a bug. – i3arnon Feb 24 '15 at 09:49
  • @HW if you wait synchronously on the returned task though with `Wait` or `Result` then you will block the UI thread. – i3arnon Feb 24 '15 at 09:50
  • you are right.. I tried this with `Task.Run(()=>{GetResponseWithRetryAsync(url, 5);});` and this actually ran without blocking my UI (this seems a bit complicated though, how can I convert my method to an `action`and pass the method to `Task.Run` right away?). Since even you are saying that simulating async-await is quite hard to get right, i will try to get as far as I can with the given tools for now to get a better understanding of their behaviour first. I hoped this would be quite easy (as you can see from my initial approach). – H W Feb 24 '15 at 10:11
  • @HW Do you mean you want to pass the synchronous `MyMethod` to `Task.Run`? you can do that just as you did with `StartNew`. `Task.Run` just uses the thread pool without you specifying `TaskScheduler.Default` – i3arnon Feb 24 '15 at 12:05
1
Task myTask = Task.Factory.Startnew(MyMethod)
myTask.ContinueWith((x)=>
{
    //do stuff when myTask is completed
},new CancellationToken(),TaskContinuationOptions.OnlyOnRanToCompletion,TaskScheduler.FromCurrentSynchronizationContext());

This means that the continue with wont block your UI thread. The main thing here is TaskScheduler.FromCurrentSynchronisationContext()

JKennedy
  • 18,150
  • 17
  • 114
  • 198
  • Apparently `OnlyOnCompleted` is not a member of `TaskContinuationOptions`? Did you mean `OnlyOnRanToCompletion`? (using your suggested settings with `OnlyOnRanToCompletion` still blocks my UI Thread though) – H W Feb 24 '15 at 09:50
  • @HW Yes I did mean `OnlyOnRanToCompletion` My Mistake. It Shouldnt Block UI Thread – JKennedy Feb 24 '15 at 09:57
  • @HW reading some of your comments seems to suggest that your bug is within MyMethod. Something within MyMethod is blocking the UI thread e.g. if you are updating any element of the UI in that thread. My example would not block the UI thread. I think it would help if you update your question with what you are trying to run in the task – JKennedy Feb 24 '15 at 10:07
  • I updated my question with the code that I tried after reading your answer. – H W Feb 24 '15 at 10:19