I'm evaluating the Async CTP.
How can I begin execution of an async function on another thread pool's thread?
static async Task Test()
{
// Do something, await something
}
static void Main( string[] args )
{
// Is there more elegant way to write the line below?
var t = TaskEx.Run( () => Test().Wait() );
// Doing much more in this same thread
t.Wait(); // Waiting for much more then just this single task, this is just an example
}
There would be, if this wasn't a console application. For example, if you do this in a Windows Forms application, you could do:
However, there is no default
SynchronizationContext
in a console, so that won't work the way you'd expect. In a console application, you need to explicitly grab the task and then wait at the end.If you're doing this in a UI thread in Windows Forms, WPF, or even a WCF service, there will be a valid SynchronizationContext that will be used to marshal back the results properly. In a console application, however, when control is "returned" at the
await
call, the program continues, and just exits immediately. This tends to mess up everything, and produce unexpected behavior.It's usually up to the method returning the
Task
to determine where it runs, if it's starting genuinely new work instead of just piggy-backing on something else.In this case it doesn't look like you really want the
Test()
method to be async - at least, you're not using the fact that it's asynchronous. You're just starting stuff in a different thread... theTest()
method could be entirely synchronous, and you could just use:That doesn't require any of the async CTP goodness.
I'm new (my virginal post) to Stack Overflow, but I'm jazzed that you're asking about the Async CTP since I'm on the team working on it at Microsoft :)
I think I understand what you're aiming for, and there's a couple of things you're doing correctly, to get you there.
What I think you want:
Task.Run vs. Task.RunEx
Because this CTP installs on top of .NET 4.0, we didn't want to patch the actual
System.Threading.Tasks.Task
type in mscorlib. Instead, the playground APIs are named FooEx when they conflicted.Why did we name some of them
Run(...)
and some of theRunEx(...)
? The reason is because of redesigns in method overloading that we hadn't completed yet by the time we released the CTP. In our current working codebase, we've actually had to tweak the C# method overloading rules slightly so that the right thing happens for Async Lambdas - which can returnvoid
,Task
, orTask<T>
.The issue is that when async method or lambdas return
Task
orTask<T>
, they actually don't have the outer task type in the return expression, because the task is generated for you automatically as part of the method or lambda's invocation. This strongly seems to us like the right experience for code clarity, though that does make things quite different before, since typically the expression of return statements is directly convertible to the return type of the method or lambda.So thus, both async
void
lambdas and asyncTask
lambdas supportreturn;
without arguments. Hence the need for a clarification in method overload resolution to decide which one to pick. Thus the only reason why you have both Run(...) and RunEx(...) was so that we would make sure to have higher quality support for the other parts of the Async CTP, by the time PDC 2010 hit.How to think about async methods/lambdas
I'm not sure if this is a point of confusion, but I thought I'd mention it - when you are writing an async method or async lambda, it can take on certain characteristics of whoever is invoking it. This is predicated on two things:
The CTP design for await and our current internal design are both very pattern-based so that API providers can help flesh out a vibrant set of things that you can 'await' on. This can vary based on the type on which you're awaiting, and the common type for that is
Task
.Task
's await implementation is very reasonable, and defers to the current thread'sSynchronizationContext
to decide how to defer work. In the case that you're already in a WinForms or WPF message loop, then your deferred execution will come back on the same message loop (as if you usedBeginInvoke()
the "rest of your method"). If you await a Task and you're already on the .NET threadpool, then the "rest of your method" will resume on one of the threadpool threads (but not necessarily the same one exactly), since they were pooled to begin with and most likely you're happy to go with the first available pool thread.Caution about using Wait() methods
In your sample you used:
var t = TaskEx.Run( () => Test().Wait() );
What that does is:
That's the primary benefit of the 'await' operator is that it allows you to add code that executes later - but without blocking the original thread. In the thread pool case, you can achieve better thread utilization.
Let me know if you have other questions about the Async CTP for VB or C#, I'd love to hear them :)