Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async and await without "threads"? Can I customize what happens under-the-hood?

Tags:

c#

async-await

I have a question about how customizable the new async/await keywords and the Task class in C# 4.5 are.

First some background for understanding my problem: I am developing on a framework with the following design:

  • One thread has a list of "current things to do" (usually around 100 to 200 items) which are stored as an own data structure and hold as a list. It has an Update() function that enumerates the list and look whether some "things" need to execute and does so. Basically its like a big thread sheduler. To simplify things, lets assume the "things to do" are functions that return the boolean true when they are "finished" (and should not be called next Update) and false when the sheduler should call them again next update.
  • All the "things" must not run concurrently and also must run in this one thread (because of thread static variables)
  • There are other threads which do other stuff. They are structured in the same way: Big loop that iterates a couple of hundret things to do in a big Update() - function.
  • Threads can send each other messages, including "remote procedure calls". For these remote calls, the RPC system is returning some kind of future object to the result value. In the other thread, a new "thing to do" is inserted.
  • A common "thing" to do are just sequences of RPCs chained together. At the moment, the syntax for this "chaining" is very verbose and complicated, since you manually have to check for the completion state of previous RPCs and invoke the next ones etc..

An example:

Future f1, f2;
bool SomeThingToDo() // returns true when "finished"
{
    if (f1 == null)
        f1 = Remote1.CallF1();
    else if (f1.IsComplete && f2 == null)
        f2 = Remote2.CallF2();
    else if (f2 != null && f2.IsComplete)
        return true;
    return false;
}

Now this all sound awefull like async and await of C# 5.0 can help me here. I haven't 100% fully understand what it does under the hood (any good references?), but as I get it from some few talks I've watched, it exactly does what I want with this nicely simple code:

async Task SomeThingToDo() // returning task is completed when this is finished.
{
    await Remote1.CallF1();
    await Remote2.CallF2();
}

But I can't find a way how write my Update() function to make something like this happen. async and await seem to want to use the Task - class which in turn seems to need real threads?

My closest "solution" so far:

The first thread (which is running SomeThingToDo) calls their functions only once and stores the returned task and tests on every Update() whether the task is completed.

Remote1.CallF1 returns a new Task with an empty Action as constructor parameter and remembers the returned task. When F1 is actually finished, it calls RunSynchronously() on the task to mark it as completed.

That seems to me like a pervertion of the task system. And beside, it creates shared memory (the Task's IsComplete boolean) between the two threads which I would like to have replaced with our remote messanging system, if possible.

Finally, it does not solve my problem as it does not work with the await-like SomeThingToDo implementation above. It seems the auto-generated Task objects returned by an async function are completed immediately?

So finally my questions:

  1. Can I hook into async/await to use my own implementations instead of Task<T>?
  2. If that's not possible, can I use Task without anything that relates to "blocking" and "threads"?
  3. Any good reference what exactly happens when I write async and await?
like image 758
Imi Avatar asked Jun 03 '13 14:06

Imi


People also ask

Does async and await create additional threads?

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. But in the remarks in description of ThreadPool class ( ThreadPool Class ): Examples of operations that use thread pool threads include the following:

Should I Mark a method as async but not use await?

The warning is exactly right: if you mark your method async but don't use await anywhere, then your method won't be asynchronous. If you call it, all the code inside the method will execute synchronously. I want write a method that should run async but don't need use await.for example when use a thread

Is async-await task executed in separate thread?

Show activity on this post. Closed 5 years ago. I am working with async-await and tasks, but I can't understand the one thing: Is async task executes in separate thread? The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread.

When should I use async void instead of async task?

You should not use async void unless it is an event handler. That is the only acceptable time and is the only correct way of making an async event handler. If your synchronous method is void, it should be async Task.


2 Answers

I haven't 100% fully understand what it does under the hood - any good references?

Back when we were designing the feature Mads, Stephen and I wrote some articles at a variety of different levels for MSDN magazine. The links are here:

http://blogs.msdn.com/b/ericlippert/archive/2011/10/03/async-articles.aspx

Start with my article, then Mads's, then Stephen's.

It seems the auto-generated Task objects returned by an async function are completed immediately?

No, they are completed when the code in the method body returns or throws, same as any other code.

Can I hook into async/await to use my own implementations instead of Task<T>?

A method which contains an await must return void, Task or Task<T>. However, the expression that is awaited can return any type so long as you can call GetAwaiter() on it. That need not be a Task.

If that's not possible, can I use Task without anything that relates to "blocking" and "threads"?

Absolutely. A Task just represents work that will complete in the future. Though that work is typically done on another thread, there is no requirement.

like image 174
Eric Lippert Avatar answered Nov 12 '22 11:11

Eric Lippert


To answer your questions:

Can I hook into async/await to use my own implementations instead of Task?

Yes. You can await anything. However, I do not recommend this.

If that's not possible, can I use Task without anything that relates to "blocking" and "threads"?

The Task type represents a future. It does not necessarily "run" on a thread; it can represent the completion of a download, or a timer expiring, etc.

Any good reference what exactly happens when I write async and await?

If you mean as far as code transformations go, this blog post has a nice side-by-side. It's not 100% accurate in its details, but it's enough to write a simple custom awaiter.


If you really want to twist async to do your bidding, Jon Skeet's eduasync series is the best resource. However, I seriously do not recommend you do this in production.

You may find my async/await intro helpful as an introduction to the async concepts and recommended ways to use them. The official MSDN documentation is also unusually good.

I did write the AsyncContext and AsyncContextThread classes that may work for your situation; they define a single-threaded context for async/await methods. You can queue work (or send messages) to an AsyncContextThread by using its Factory property.

like image 36
Stephen Cleary Avatar answered Nov 12 '22 10:11

Stephen Cleary