Monday 6 November 2017

c# - Run an async function in another thread

itemprop="text">


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
}

class="post-text" itemprop="text">
class="normal">Answer



I'm new
(my virginal post) to , 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:



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(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 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 the
RunEx(...)? 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 return
void, Task, or
Task.



The
issue is that when async method or lambdas return Task or
Task, 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 async Task lambdas
support return; 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 type on which
    you are awaiting

  • And possibly the synchronization context
    (depending on above)



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's
SynchronizationContext 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 used
BeginInvoke() 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:





  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 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
:)



No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...