[ACCEPTED]-Run an async function in another thread-async-ctp

Accepted answer
Score: 52

I'm new (my virginal post) to Stack Overflow, but 69 I'm jazzed that you're asking about the 68 Async CTP since I'm on the team working 67 on it at Microsoft :)

I think I understand 66 what you're aiming for, and there's a couple 65 of things you're doing correctly, to get 64 you there.

What I think you want:

static async Task Test()
    // Do something, await something

static void Main(string[] args)
    // In the CTP, use Task.RunEx(...) to run an Async Method or Async Lambda
    // on the .NET thread pool
    var t = TaskEx.RunEx(Test);
    // the above was just shorthand for
    var t = TaskEx.RunEx(new Func<Task>(Test));
    // because the C# auto-wraps methods into delegates for you.

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example

Task.Run vs. Task.RunEx

Because 63 this CTP installs on top of .NET 4.0, we 62 didn't want to patch the actual System.Threading.Tasks.Task type in mscorlib. Instead, the 61 playground APIs are named FooEx when they 60 conflicted.

Why did we name some of them 59 Run(...) and some of the RunEx(...)? The reason is because 58 of redesigns in method overloading that 57 we hadn't completed yet by the time we released 56 the CTP. In our current working codebase, we've 55 actually had to tweak the C# method overloading 54 rules slightly so that the right thing happens 53 for Async Lambdas - which can return void, Task, or 52 Task<T>.

The issue is that when async method or 51 lambdas return Task or Task<T>, they actually don't 50 have the outer task type in the return expression, because 49 the task is generated for you automatically 48 as part of the method or lambda's invocation. This 47 strongly seems to us like the right experience 46 for code clarity, though that does make 45 things quite different before, since typically 44 the expression of return statements is directly 43 convertible to the return type of the method 42 or lambda.

So thus, both async void lambdas and 41 async Task lambdas support return; without arguments. Hence 40 the need for a clarification in method overload 39 resolution to decide which one to pick. Thus 38 the only reason why you have both Run(...) and 37 RunEx(...) was so that we would make sure 36 to have higher quality support for the other 35 parts of the Async CTP, by the time PDC 34 2010 hit.

How to think about async methods/lambdas

I'm not sure if this is a point 33 of confusion, but I thought I'd mention 32 it - when you are writing an async method 31 or async lambda, it can take on certain 30 characteristics of whoever is invoking it. This 29 is predicated on two things:

  • The type on which you are awaiting
  • And possibly the synchronization context (depending on above)

The CTP design 28 for await and our current internal design 27 are both very pattern-based so that API 26 providers can help flesh out a vibrant set 25 of things that you can 'await' on. This 24 can vary based on the type on which you're 23 awaiting, and the common type for that is 22 Task.

Task's await implementation is very reasonable, and 21 defers to the current thread's SynchronizationContext to decide 20 how to defer work. In the case that you're 19 already in a WinForms or WPF message loop, then 18 your deferred execution will come back on 17 the same message loop (as if you used BeginInvoke() the 16 "rest of your method"). If you await a Task 15 and you're already on the .NET threadpool, then 14 the "rest of your method" will resume on 13 one of the threadpool threads (but not necessarily 12 the same one exactly), since they were pooled 11 to begin with and most likely you're happy 10 to go with the first available pool thread.

Caution about using Wait() methods

In 9 your sample you used: var t = TaskEx.Run( () => Test().Wait() );

What that does is:

  1. In the surrounding thread synchronously call TaskEx.Run(...) to execute a lambda on the thread pool.
  2. A thread pool thread is designated for the lambda, and it invokes your async method.
  3. The async method Test() is invoked from the lambda. Because the lambda was executing on the thread pool, any continuations inside Test() can run on any thread in the thread pool.
  4. The lambda doesn't actually vacate that thread's stack because it had no awaits in it. The TPL's behavior in this case depends on if Test() actually finished before the Wait() call. However, in this case, there's a real possibility that you will be blocking a thread pool thread while it waits for Test() to finish executing on a different thread.

That's 8 the primary benefit of the 'await' operator 7 is that it allows you to add code that executes 6 later - but without blocking the original 5 thread. In the thread pool case, you can 4 achieve better thread utilization.

Let me 3 know if you have other questions about the 2 Async CTP for VB or C#, I'd love to hear 1 them :)

Score: 6

It's usually up to the method returning 10 the Task to determine where it runs, if it's 9 starting genuinely new work instead of just 8 piggy-backing on something else.

In this 7 case it doesn't look like you really want the 6 Test() method to be async - at least, you're not 5 using the fact that it's asynchronous. You're 4 just starting stuff in a different thread... the 3 Test() method could be entirely synchronous, and 2 you could just use:

Task task = TaskEx.Run(Test);
// Do stuff

That doesn't require 1 any of the async CTP goodness.

Score: 2

There would be, if this wasn't a console 15 application. For example, if you do this 14 in a Windows Forms application, you could 13 do:

// Added to a button click event, for example
public async void button1_Click(object sender, EventArgs e)
    // Do some stuff
    await Test();
    // Do some more stuff

However, there is no default SynchronizationContext in a console, so 12 that won't work the way you'd expect. In 11 a console application, you need to explicitly 10 grab the task and then wait at the end.

If 9 you're doing this in a UI thread in Windows 8 Forms, WPF, or even a WCF service, there 7 will be a valid SynchronizationContext that 6 will be used to marshal back the results 5 properly. In a console application, however, when 4 control is "returned" at the await call, the 3 program continues, and just exits immediately. This 2 tends to mess up everything, and produce 1 unexpected behavior.

More Related questions