Search code examples
c#windows-phonewindows-phone-7.1async-await

Run "async" method on a background thread


I'm trying to run an "async" method from an ordinary method:

public string Prop
{
    get { return _prop; }
    set
    {
        _prop = value;
        RaisePropertyChanged();
    }
}

private async Task<string> GetSomething()
{
    return await new Task<string>( () => {
        Thread.Sleep(2000);
        return "hello world";
    });
}

public void Activate()
{
    GetSomething.ContinueWith(task => Prop = task.Result).Start();
    // ^ exception here
}

The exception thrown is:

Start may not be called on a continuation task.

What does that mean, anyway? How can I simply run my async method on a background thread, dispatch the result back to the UI thread?

Edit

Also tried Task.Wait, but the waiting never ends:

public void Activate()
{
    Task.Factory.StartNew<string>( () => {
        var task = GetSomething();
        task.Wait();

        // ^ stuck here

        return task.Result;
    }).ContinueWith(task => {
        Prop = task.Result;
    }, TaskScheduler.FromCurrentSynchronizationContext());
    GetSomething.ContinueWith(task => Prop = task.Result).Start();
}

Solution

  • To fix your example specifically:

    public void Activate()
    {
        Task.Factory.StartNew(() =>
        {
            //executes in thread pool.
            return GetSomething(); // returns a Task.
        }) // returns a Task<Task>.
        .Unwrap() // "unwraps" the outer task, returning a proxy
                  // for the inner one returned by GetSomething().
        .ContinueWith(task =>
        {
            // executes in UI thread.
            Prop = task.Result;
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }
    

    This will work, but it's old-school.

    The modern way to run something on a background thread and dispatch back to UI thread is to use Task.Run(), async, and await:

    async void Activate()
    {
        Prop = await Task.Run(() => GetSomething());
    }
    

    Task.Run will start something in a thread pool thread. When you await something, it automatically comes back in on the execution context which started it. In this case, your UI thread.

    You should generally never need to call Start(). Prefer async methods, Task.Run, and Task.Factory.StartNew -- all of which start the tasks automatically. Continuations created with await or ContinueWith are also started automatically when their parent completes.