4

I'm having a hard time understanding async and the corresponding behavior. This is a model of the program I'm trying to write, and a repro of the problem I'm having:

namespace AsyncTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("starting....");
            MyClass myClass = new MyClass();
            myClass.DoSomeWork();
            Console.WriteLine("ending....");
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public bool DoSomeWork()
        {
            return GetTrue().Result;
        }

        Task<bool> GetTrue()
        {
            return new Task<bool>(() => true);
        }
    }
}

When I make the GetTrue().Result call, it never returns. My Task<bool> GetTrue() method is a dummy method for repro purposes, but I'm not quite sure why it never comes back. But it returns if I have this:

namespace AsyncTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("starting....");
            MyClass myClass = new MyClass();
            myClass.DoSomeWork();
            Console.WriteLine("ending....");
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public async void DoSomeWork()
        {
            await GetTrue();
        }

        Task<bool> GetTrue()
        {
            return new Task<bool>(() => true);
        }
    }
}

The problem is, with my repro I want the initial behavior. I don't want async void for the calling method. I want to be able to return bool from my calling "normal" method. How can I achieve this? How can I get the first example to run and actually return the bool from DoSomeWork()?

Thank you in advance.

EDIT: Thank you for the answers. I think what the problem is, is that my GetTrue() method isn't a true repro. In my method, I'm not generating the actual Task<bool> object. It's really a call to Task.Factory.FromAsync<bool>() that is creating the Task<bool> object. And it's that task that I don't know how to "start". Any other ideas? Thanks!

user3175663
  • 107
  • 4
  • Your edit changes the concept of the question. If you're trying to block (with `task.Result`) on the result of `FromAsync`, you'd need to clarify what is the actual context of this code. Is it really a simple console app, or perhaps, it is a WinForms / WPF / ASP.NET app? Then it might explain why are you're having a deadlock. Check Stephen Cleary's blog: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – noseratio May 12 '14 at 11:57
  • @Noseratio Sorry, the context is a WCF service. The `Task.Factory.FromAsync()` call is wrapping the `Begin...()` and `End...()` functions that the service has pushed to the consumer. But I see no way to actually start the task and it just hangs. – user3175663 May 12 '14 at 12:47
  • So this is still the *client-side* where you call a WCF service? If so, it still matters what kind of app is this client-side app. – noseratio May 12 '14 at 12:52
  • @Noseratio That's correct, it is the client-side making the WCF call. The client-side app is a Windows Phone 8 application. – user3175663 May 12 '14 at 12:55
  • This is a frequent mistake, I've updated my answer with more details about it. – noseratio May 12 '14 at 13:14

2 Answers2

4

You're probably looking for:

Task<bool> GetTrue()
{
    return Task.FromResult(true);
}

instead of:

Task<bool> GetTrue()
{
    return new Task<bool>(() => true);
}

In the latter case, you don't even start the task you create. You'd need to start it with Task.Run or Task.RunSynchronously. Usually you don't create tasks directly via new Task(). Check Stephen Toub's "Task.Factory.StartNew" vs "new Task(...).Start" and "Task.Run vs Task.Factory.StartNew".

Depending on what you're trying to achieve, you might be tempted to do this:

Task<bool> GetTrue()
{
    return Task.Run(() => true);
}

However, usually this turns to be a known-anti-pattern, check "Should I expose asynchronous wrappers for synchronous methods?".

Updated to address the comments:

... the context is a WCF service. The Task.Factory.FromAsync() call is wrapping the Begin...() and End...() functions that the service has pushed to the consumer. But I see no way to actually start the task and it just hangs. ... it is the client-side making the WCF call. The client-side app is a Windows Phone 8 application.

You're experiencing a deadlock on the UI thread, because you're blocking on the result of a pending task (with task.Result). The task itself cannot be completed, because the await continuation is posted asynchronously to the dispatcher loop of the UI thread of your WP8 app, which is now blocked. Stephen Cleary explains this in great details in his blog. To avoid this, you should embrace "async all the way down" programming model. It practically means that you should make your UI event and command handlers async as well, and do var result = await task, rather than var result = task.Result in there.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 2
    +1 for the anti-pattern. Stephen Cleary also has a nice blog post on this IIRC. It basically says "let consumers of your API wrap things in Tasks.. don't do it yourself". – Simon Whitehead May 12 '14 at 02:04
  • Thank you for the answer! (Please see my answer edit) I'm not actually creating the `Task` (apologies, bad repro I believe). The `Task` is getting generated from a call to `Task.Factory.FromAsync()`. And it's *that* task that I'm not sure how to "start". Any thoughts? Thanks in advance! Also, +1. – user3175663 May 12 '14 at 11:51
  • @user3175663, see my comment to the question. – noseratio May 12 '14 at 12:01
  • Ok perfect, I'll try that. With `async` event handlers, though, I thought `async void` was bad practice. Am I mistaken? – user3175663 May 12 '14 at 13:25
  • @user3175663, `async void` event handlers are probably the most notable exception of that rule, check [this](http://stackoverflow.com/q/19415646/1768303). Just deal properly with [exceptions](http://stackoverflow.com/a/22395161/1768303) and [reentracy](http://blogs.msdn.com/b/lucian/archive/2014/03/03/async-re-entrancy-and-the-patterns-to-deal-with-it.aspx) inside `async void` event handlers. – noseratio May 12 '14 at 13:29
2

You need to start a task before it does anything.. calling Run will do that for you:

Task<bool> GetTrue()
{
    return Task.Run(() => true);
}

This gives the output you expect. Result is a blocking call. If your Task instance never starts.. Result can never return.

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • Thank you for the answer! (Please see my answer edit) I'm not actually creating the `Task` (apologies, bad repro I believe). The `Task` is getting generated from a call to `Task.Factory.FromAsync()`. And it's *that* task that I'm not sure how to "start". Any thoughts? Thanks in advance! +1. – user3175663 May 12 '14 at 11:51