3

I'm trying to do some time consuming actions without freezing the GUI Thread. The UpdateSomething Method is called in two places once in the constructor of my ViewModel and once on button click (RelayCommand). The Method is executed in a WorkerThread if the method is called via RelayCommand, but runs in the MainThread (GUI) when the method call comes from the constructor.

What causes this strange behavior? I double check a few times via IntelliTrace.

This is the method in question:

private async void UpdateSomething()
{
    var item = await Task.Factory.StartNew(() =>this.DoSomething("I should run async"));
    this.TestItem = item;
}

I'm using WPF and .Net 4.5

Joel
  • 4,862
  • 7
  • 46
  • 71

3 Answers3

4

I ran into the same problem a while ago. I stuck up a little post about it here:

Today I had a major hair pulling moment. I was using a await/async on a block of code wrapped in a new task. No matter what I did it would just start the Task but then not wait for the result.

After much frustration I worked out that using await Task.Factory.StartNew doesn't play well with await.

In the end I simply changed it to Task.Run what was then worked fine.

Basically you need to use Task.Run instead because await doesn't play nicely with Task.Factory.StartNew

Community
  • 1
  • 1
pingoo
  • 2,074
  • 14
  • 17
  • For an explanation of the differences, see [`Task.Run` vs `Task.Factory.StartNew`](http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx). – Stephen Cleary May 16 '13 at 11:34
4

For the case where the Tasks action runs on the main thread, the most likely cause is that the method UpdateSomething is being called from within another Task (a Task that was scheduled to run on the main thread). In that case, the TaskScheduler.Current is the main thread TaskScheduler not the TaskScheduler.Default which queues work to the thread pool.

Task.Factory.StartNew defaults to using TaskScheduler.Current (you can change what it uses by calling the appropriate override), while Task.Run uses TaskScheduler.Default.

The statement that Task.Factory.StartNew doesn't play will with async/await is not correct, it's just that some of the defaults are probably not what you want for the common case (this is part of whyTask.Run was introduced).

See:

Community
  • 1
  • 1
Matt Smith
  • 17,026
  • 7
  • 53
  • 103
0

This question was answered by @Rene147 but he deleted his answer. (for whatever reason)

I will briefly rephrase what he had written:

await/asycn doesn't play very well with Task.Factory.StartNew().

Simply replace Task.Factory.StartNew by Task.Run and everything should work:

private async void UpdateSomething()
{
    //not working when call comes from constructor
    //var item = await Task.Factory.StartNew(() =>this.DoSomething("I should run async"));
    //working
    var item = await Task.Run(() =>this.DoSomething("I run async!"));
    this.TestItem = item;
}

Rene147 also wrote a short blogpost about it.

Would be happy to get more information about this issue, still seems odd to me.

Joel
  • 4,862
  • 7
  • 46
  • 71
  • 1
    A moderator deleted Rene's answer... I'm guessing it's because someone flagged it as a link-only answer or spam, so I made an edit to the answer to move the content from the link into the answer, and have flagged it to be undeleted. Moderators really shouldn't be deleting useful, upvoted, and accepted answers to questions, especially when they are the only answer there, and they actually answer the question. – Rachel May 16 '13 at 13:32