Need an execution context for combination of async and TPL code - c#

I'm looking for an execution context which plays nicely with async/await and with the TPL at the same time in the following way (expected behavior):
async Task<string> ReadContext(string slot)
{
// Perform some async code
...
return Context.Read(slot);
}
(1) Playing nicely with async/await
Context.Store("slot", "value");
await DoSomeAsync();
Assert.AreEqual("value", Context.Read("slot"));
Context.Store("slot", "value");
var value = await ReadContext("slot");
Assert.AreEqual("value", value);
(2) Playing nicely with Task.Run()
Context.Store("slot", "value");
var task = Task.Run(() => ReadContext("slot"));
Assert.IsNull(task.Result);
(3) Playing nicely with an awaited Task
Context.Store("slot", "value");
var value = await Task.Run(() => ReadContext("slot"));
Assert.AreEqual("value", value);
(3) is not essential but would be nice. I use CallContext now but it fails at (2) as the values stored in it are accessible even in manually ran tasks, even in those ran using Task.Factory.StartNew(..., LongRunning) which should enforce running the task on a separate thread.
Is there any way to accomplish that?

Your real question is in your comment:
I need a place to store NHibernate sessions in ASP.NET application. HttpContext works fine (and respects async/await) if I'm inside a request context, but it is unavailable once I jump into manually ran tasks.
First off, you should be avoiding "manually run tasks" in an ASP.NET application at all; I have a blog post on the subject.
Secondly, storing things in HttpContext.Items is sort of a hack. It can be useful in a handful of situations, but IMO managing NHibernate sessions is something that should be designed properly into your application. That means you should be passing around the session (or a service providing access to the session) either in your method calls or injected into each type that needs it.
So, I really think that a "context" like you're looking for is a wrong solution. Even if it were possible, which it is not.
As #Noseratio noted, requirements (2) and (3) cannot both be met. Either the code executing in the Task.Run has access or it does not; it can't be both.
As you've discovered, requirements (1) and (3) can be met by the logical call context (note to Googlers: this only works under .NET 4.5 and only if you store immutable data; details on my blog).
There is no easy way to satisfy (1) and (2) unless you manually remove the data (FreeNamedDataSlot) at the beginning of the code in Task.Run. I think there may be another solution but it would require custom awaitables at every await, which is completely cumbersome, brittle, and unmaintainable.

Related

Chaining arbitrary number of tasks together in C#.NET

What I have
I have a set of asynchronous processing methods, similar to:
public class AsyncProcessor<T>
{
//...rest of members, etc.
public Task Process(T input)
{
//Some special processing, most likely inside a Task, so
//maybe spawn a new Task, etc.
Task task = Task.Run(/* maybe private method that does the processing*/);
return task;
}
}
What I want
I would like to chain them all together, to execute in sequential order.
What I tried
I have tried to do the following:
public class CompositeAsyncProcessor<T>
{
private readonly IEnumerable<AsyncProcessor<T>> m_processors;
//Constructor receives the IEnumerable<AsyncProcessor<T>> and
//stores it in the field above.
public Task ProcessInput(T input)
{
Task chainedTask = Task.CompletedTask;
foreach (AsyncProcessor<T> processor in m_processors)
{
chainedTask = chainedTask.ContinueWith(t => processor.Process(input));
}
return chainedTask;
}
}
What went wrong
However, tasks do not run in order because, from what I have understood, inside the call to ContinueWith, the processor.Process(input) call is performed immediately and the method returns independently of the status of the returned task. Therefore, all processing Tasks still begin almost simultaneously.
My question
My question is whether there is something elegant that I can do to chain the tasks in order (i.e. without execution overlap). Could I achieve this using the following statement, (I am struggling a bit with the details), for example?
chainedTask = chainedTask.ContinueWith(async t => await processor.Process(input));
Also, how would I do this without using async/await, only ContinueWith?
Why would I want to do this?
Because my Processor objects have access to, and request things from "thread-unsafe" resources. Also, I cannot just await all the methods because I have no idea about how many they are, so I cannot just write down the necessary lines of code.
What do I mean by thread-unsafe? A specific problem
Because I may be using the term incorrectly, an illustration is a bit better to explain this bit. Among the "resources" used by my Processor objects, all of them have access to an object such as the following:
public interface IRepository
{
void Add(object obj);
bool Remove(object obj);
IEnumerable<object> Items { get; }
}
The implementation currently used is relatively naive. So some Processor objects add things, while others retrieve the Items for inspection. Naturally, one of the exceptions I get all too often is:
InvalidOperationException: Collection was modified, enumeration
operation may not execute.
I could spend some time locking access and pre-running the enumerations. However, this was the second option I would get down to, while my first thought was to just make the processes run sequentially.
Why must I use Tasks?
While I have full control in this case, I could say that for the purposes of the question, I might not be able to change the base implementation, so what would happen if I were stuck with Tasks? Furthermore, the operations actually do represent relatively time-consuming CPU-bound operations plus I am trying to achieve a responsive user interface so I needed to unload some burden to asynchronous operations. While being useful and, in most of my use-cases, not having the necessity to chain multiple of them, rather a single one each time (or a couple, but always specific and of a specific count, so I was able to hook them together without iterations and async/await), one of the use-cases finally necessitated chaining an unknown number of Tasks together.
How I deal with this currently
The way I am dealing with this currently is to append a call to Wait() inside the ContinueWith call, i.e.:
foreach (AsyncProcessor<T> processor in m_processors)
{
chainedTask = chainedTask.ContinueWith(t => processor.Process(input).Wait());
}
I would appreciate any idea on how I should do this, or how I could do it more elegantly (or, "async-properly", so to speak). Also, I would like to know how I can do this without async/await.
Why my question is different from this question, which did not answer my question entirely.
Because the linked question has two tasks, so the solution is to simply write the two lines required, while I have an arbitrary (and unknown) number of tasks, so I need an suitable iteration. Also, my method is not async. I now understand (from the single briefly available answer, which was deleted) that I could do it fairly easily if I changed my method to async and await each processor's Task method, but I still wish to know how this could be achieved without async/await syntax.
Why my question is not a duplicate of the other linked questions
Because none of them explains how to chain correctly using ContinueWith and I am interested in a solution that utilizes ContinueWith and does not make use of the async/await pattern. I know this pattern may be the preferable solution, I want to understand how to (if possible) make arbitrary chaining using ContinueWith calls properly. I now know I don't need ContinueWith. The question is, how do I do it with ContinueWith?
foreach + await will run Processes sequentially.
public async Task ProcessInputAsync(T input)
{
foreach (var processor in m_processors)
{
await processor.Process(input));
}
}
Btw. Process, should be called ProcessAsync
The method Task.ContinueWith does not understand async delegates, like Task.Run do, so when you return a Task it considers this as a normal return value and wraps it in another Task. So you end up receiving a Task<Task> instead of what you expected to get. The problem would be obvious if the AsyncProcessor.Process was returning a generic Task<T>. In this case you would get a compile error because of the illegal casting from Task<Task<T>> to Task<T>. In your case you cast from Task<Task> to Task, which is legal, since Task<TResult> derives from Task.
Solving the problem is easy. You just need to unwrap the Task<Task> to a simple Task, and there is a built-in method Unwrap that does exactly that.
There is another problem that you need to solve though. Currently your code suppresses all exceptions that may occur on each individual AsyncProcessor.Process, which I don't think it was intended. So you must decide which strategy to follow in this case. Are you going to propagate the first exception immediately, or you prefer to cache them all and propagate them at the end bundled in an AggregateException, like the Task.WhenAll does? The example bellow implements the first strategy.
public class CompositeAsyncProcessor<T>
{
//...
public Task Process(T input)
{
Task current = Task.CompletedTask;
foreach (AsyncProcessor<T> processor in m_processors)
{
current = current.ContinueWith(antecessor =>
{
if (antecessor.IsFaulted)
return Task.FromException<T>(antecessor.Exception.InnerException);
return processor.Process(input);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default
).Unwrap();
}
return current;
}
}
I have used an overload of ContinueWith that allows configuring all the options, because the defaults are not ideal. The default TaskContinuationOptions is None. Configuring it to ExecuteSynchronously you minimize the thread switches, since each continuation will run in the same thread that completed the previous one.
The default task scheduler is TaskScheduler.Current. By specifying TaskScheduler.Default you make it explicit that you want the continuations to run in thread-pool threads (for some exceptional cases that won't be able to run synchronously). The TaskScheduler.Current is context specific, and if it ever surprises you it won't be in a good way.
As you see there are a lot of gotchas with the old-school ContinueWith approach. Using the modern await in a loop is a lot easier to implement, and a lot more difficult to get it wrong.

Should I use synchronous or asynchronous methods of c# mongodb driver?

In c# mongodb driver, there are both synchronous and asynchronous methods available as shown below?
_mongoCollection.InsertOneAsync(entity);
_mongoCollection.Insert(entity);
I believe, in majority of cases, the amount of work done asynchronously in data access layer is very less. So I am awaiting the database calls immediately as follows:
await _mongoCollection.InsertOneAsync(entity);
await _mongoCollection.DeleteOneAsync(query);
await _mongoCollection.Find(query).ToListAsync();
Now my question is: As i am awaiting the db calls immediately, I am not seeing any use of async methods here. So, should I use async methods? (or) Should i use synchronous methods?
I think the prior answers are missing a major point of asynchrony; namely that when you use the async methods**, you free up the thread you're on while (most of) the I/O operation completes, and it is available for use elsewhere in your app; e.g. servicing other requests.
Let's suppose the Mongo database is on another server. Even with a quick network and quick Mongo instance, that's still perhaps 5ms per command, which on a 2GHz CPU is 10 million clock cycles. While you're not going to get all of those to use on running your code, due to various overheads, you will get still get an advantage if you have an app that can use the additional threads productively.
So if your app is for instance a website, API, or UI, I would use the async methods. If it's a single thread console app, you probably won't gain any of the above benefit.
--
** This assumes the Mongo async method are truly async, and not just wrappers for the sync methods.
that depends on if you have any use of result in next statement or not.
for e.g.
void async myMethod(){
var result = funcAsync();
// do some additional work..
var data = await result;
.
.
}
above is scenario where you should use async await.
Now let assume if you have method;
void async myMethod(){
var result = await funcAsync(); // here result is required before any other logic ahead
.
.
.
}
here you really don't need to use async await, but what if in near future your system changed and you need to add additional code in between, then you might need to change method signatures and corresponding location where that method is called.
So this is totally depends on the requirement.
It really depends on what are your requirements.
From the code you've posted I can guess that you don't need an asynchronous call but it may come in handy with the last call _mongoCollection.Find(...).ToListAsync()
Asynchronous calls ( IMHO ) should be used only when you have some independent/complex logic. In example that you showed I think you can only await the last call making it :
_mongoCollection.Insert(entity);
_mongoCollection.Find(query).ToListAsync();
This way you can simply do some extra logic while awaiting ToListAsync() :
_mongoCollection.Insert(entity);
Task<List<object>> _task = _mongoCollection.Find(query).ToListAsync();
// my complex logic/calculations and stuff
await _task;
Being 100% honest I don't see any other reason to use asynchronous calls.
My opiniion is that from your example, usage of asynchronous call is just useless.

Using the result of an async method

I have a simple async method with the signature:
public async Task<bool> AllowAccessAsync(string authenticatedUserToken)
When calling this method, I appear to have two options when assigning its result to a local variable:
bool response = null;
// option 1
await this.transportService.AllowAccessAsync(authenticatedUserToken).ContinueWith(task => response = task.Result);
// option 2
response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
The first uses a continuation delegate to assign to the local variable, the second assigns the result directly to the variable.
Do these have the same result? Are there any advantages to either approach? Is there a better way to do this?
Do these have the same result?
Edit:
#Servy points out correctly that since the ContinueWith is simply a projection of the result. This means that both operations are semantically equivalent, but exception wise they'll behave differently.
Are there any advantages to either approach? Is there a better way to do this?
Edit 2:
The below remark relate to the use of async-await vs ContinueWith, generally. Looking specifically at both examples as they both use async-await, definitely use the latter, as both contain state-machine generation, but the latter will propagate an AggregateException in case an exception occurs.
async-await has the minimal overhead of producing a state-machine, while ContinueWith doesn't. On the other hand, using async-await lets you "feel" synchronous while actually being asynchronous, and saves you the verbosity of ContinueWith. I would definitely go with async-await, though I advise you to research the right path of using it, as there can be unexpected pitifuls.
By using async/await with Task Parallel Library's ContinueWith you're mixing patterns unnecessarily. There's a whole host of reasons not to do this unless you have no choice, not least of which is that async/await's SynchronizationContext is not preserved by the default implementation of ContinueWith.
So in short, option 2 is correct.
Do these have the same result?
Yes, both options will eventually set the same result into response. The only difference happens when there's an exception. In the first option The exception thrown will be an AggregateException wrapper over the actual exception while in the second one it will be the actual exception.
Are there any advantages to either approach?
There is absolutely no advantage to use ContinueWith in such a way. The second option has the advantages of having better exception handling and being much simpler to write and read.
Is there a better way to do this?
Not really, this is how you use async-await.
As Servy commented, this wasn't how ContinueWith was meant to be used. The ContinueWith equivalent is to put the rest of the method into the continuation. So instead of this:
public async Task FooAsync()
{
var response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
Console.WriteLine(response);
}
You would do this:
public Task FooAsync()
{
return this.transportService.AllowAccessAsync(authenticatedUserToken).
ContinueWith(task => Console.WriteLine(task.GetAwaiter().GetResult()));
}
This does have some performance advantages as it doesn't need the async-await state machine but it's very complicated to get right.
It has more to do with coding style.
The first style might be useful when you have a large workflow which might want to assign different continuations when executing the promise. However it relies on closures. I would not recommend this usage since it's not always predictable. The ContinueWith should be used when you create workflows and each step relies only on the previous ones, and does not communicate with the outside scope, except by delivering a task that yields the final result (which you would then await).
The second one is useful when you are just interested in the result.
Also ContinueWith allows you to specify a TaskScheduler since the one that comes default with your application might not be the one you want.
More information on TaskScheduler here:
http://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html

Should we use ConfigureAwait(false) in libraries that call async callbacks?

There are lots of guidelines for when to use ConfigureAwait(false), when using await/async in C#.
It seems the general recommendation is to use ConfigureAwait(false) in library code, as it rarely depends on the synchronization context.
However, assume we are writing some very generic utility code, which takes a function as input. A simple example could be the following (incomplete) functional combinators, to make simple task-based operations easier:
Map:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
FlatMap:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
The question is, should we use ConfigureAwait(false) in this case? I am unsure how the context capture works wrt. closures.
On one hand, if the combinators are used in a functional way, the synchronization context should not be necessary. On the other hand, people might misuse the API, and do context dependent stuff in the provided functions.
One option would be to have separate methods for each scenario (Map and MapWithContextCapture or something), but it feels ugly.
Another option might be to add the option to map/flatmap from and into a ConfiguredTaskAwaitable<T>, but as awaitables don't have to implement an interface this would result in a lot of redundant code, and in my opinion be even worse.
Is there a good way to switch the responsibility to the caller, such that the implemented library doesn't need to make any assumptions on whether or not the context is needed in the provided mapping-functions?
Or is it simply a fact, that async methods don't compose too well, without various assumptions?
EDIT
Just to clarify a few things:
The problem does exist. When you execute the "callback" inside the utility function, the addition of ConfigureAwait(false) will result in a null sync. context.
The main question is how we should tackle the situation. Should we ignore the fact that someone might want to use the sync. context, or is there a good way to shift the responsibility out to the caller, apart from adding some overload, flag or the like?
As a few answers mention, it would be possible to add a bool-flag to the method, but as I see it, this is not too pretty either, as it will have to be propagated all the way through the API's (as there are more "utility" functions, depending on the ones shown above).
When you say await task.ConfigureAwait(false) you transition to the thread-pool causing mapping to run under a null context as opposed to running under the previous context. That can cause different behavior. So if the caller wrote:
await Map(0, i => { myTextBox.Text = i.ToString(); return 0; }); //contrived...
Then this would crash under the following Map implementation:
var result = await task.ConfigureAwait(false);
return await mapper(result);
But not here:
var result = await task/*.ConfigureAwait(false)*/;
...
Even more hideous:
var result = await task.ConfigureAwait(new Random().Next() % 2 == 0);
...
Flip a coin about the synchronization context! This looks funny but it is not as absurd as it seems. A more realistic example would be:
var result =
someConfigFlag ? await GetSomeValue<T>() :
await task.ConfigureAwait(false);
So depending on some external state the synchronization context that the rest of the method runs under can change.
This also can happen with very simple code such as:
await someTask.ConfigureAwait(false);
If someTask is already completed at the point of awaiting it there will be no switch of context (this is good for performance reasons). If a switch is necessary then the rest of the method will resume on the thread pool.
This non-determinism a weakness of the design of await. It's a trade-off in the name of performance.
The most vexing issue here is that when calling the API is is not clear what happens. This is confusing and causes bugs.
What to do?
Alternative 1: You can argue that it is best to ensure deterministic behavior by always using task.ConfigureAwait(false).
The lambda must make sure that it runs under the right context:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
Map(..., async x => await Task.Factory.StartNew(
() => { /*access UI*/ },
CancellationToken.None, TaskCreationOptions.None, uiScheduler));
It's probably best to hide some of this in a utility method.
Alternative 2: You can also argue that the Map function should be agnostic to the synchronization context. It should just leave it alone. The context will then flow into the lambda. Of course, the mere presence of a synchronization context might alter the behavior of Map (not in this particular case but in general). So Map has to be designed to handle that.
Alternative 3: You can inject a boolean parameter into Map that specifies whether to flow the context or not. That would make the behavior explicit. This is sound API design but it clutters the API. It seems inappropriate to concern a basic API such as Map with synchronization context issues.
Which route to take? I think it depends on the concrete case. For example, if Map is a UI helper function it makes sense to flow the context. If it is a library function (such as a retry helper) I'm not sure. I can see all alternatives make sense. Normally, it is recommended to apply ConfigureAwait(false) in all library code. Should we make an exception in those cases where we call user callbacks? What if we have already left the right context e.g.:
void LibraryFunctionAsync(Func<Task> callback)
{
await SomethingAsync().ConfigureAwait(false); //Drops the context (non-deterministically)
await callback(); //Cannot flow context.
}
So unfortunately, there is no easy answer.
The question is, should we use ConfigureAwait(false) in this case?
Yes, you should. If the inner Task being awaited is context aware and does use a given synchronization context, it would still be able to capture it even if whoever is invoking it is using ConfigureAwait(false). Don't forget that when disregarding the context, you're doing so in the higher level call, not inside the provided delegate. The delegate being executed inside the Task, if needed, will need to be context aware.
You, the invoker, have no interest in the context, so it's absolutely fine to invoke it with ConfigureAwait(false). This effectively does what you want, it leaves the choice of whether the internal delegate will include the sync context up to the caller of your Map method.
Edit:
The important thing to note is that once you use ConfigureAwait(false), any method execution after that would be on on an arbitrary threadpool thread.
A good idea suggested by #i3arnon would be to accept an optional bool flag indicating whether context is needed or not. Although a bit ugly, would be a nice work around.
I think the real issue here comes from the fact that you are adding operations to Task while you actually operate on the result of it.
There's no real reason to duplicate these operations for the task as a container instead of keeping them on the task result.
That way you don't need to decide how to await this task in a utility method as that decision stays in the consumer code.
If Map is instead implemented as follows:
public static TResult Map<T, TResult>(this T value, Func<T, TResult> mapping)
{
return mapping(value);
}
You can easily use it with or without Task.ConfigureAwait accordingly:
var result = await task.ConfigureAwait(false)
var mapped = result.Map(result => Foo(result));
Map here is just an example. The point is what are you manipulating here. If you are manipulating the task, you shouldn't await it and pass the result to a consumer delegate, you can simply add some async logic and your caller can choose whether to use Task.ConfigureAwait or not. If you are operating on the result you don't have a task to worry about.
You can pass a boolean to each of these methods to signify whether you want to continue on the captured context or not (or even more robustly pass an options enum flags to support other await configurations). But that violates separation of concerns, as this doesn't have anything to do with Map (or its equivalent).

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

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:
Can I hook into async/await to use my own implementations instead of Task<T>?
If that's not possible, can I use Task without anything that relates to "blocking" and "threads"?
Any good reference what exactly happens when I write async and await?
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.
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.
Can I hook into async/await to use my own implementations instead of Task?
Yes.
If that's not possible, can I use Task without anything that relates to "blocking" and "threads"?
Yes.
Any good reference what exactly happens when I write async and await?
Yes.
I would discourage you from asking yes/no questions. You probably don't just want yes/no answers.
async and await seem to want to use the Task - class which in turn seems to need real threads?
Nope, that's not true. A Task represents something that can be completed at some point in the future, possibly with a result. It's sometimes the result of some computation in another thread, but it doesn't need to be. It can be anything that is happening at some point in the future. For example, it could be the result of an IO operation.
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.
So what you're missing here is the TaskCompletionSource class. With that missing puzzle piece a lot should fit into place. You can create the TCS object, pass the Task from it's Task property around to...whomever, and then use the SetResult property to signal it's completion. Doing this doesn't result in the creation of any additional threads, or use the thread pool.
Note that if you don't have a result and just want a Task instead of a Task<T> then just use a TaskCompletionSource<bool> or something along those lines and then SetResult(false) or whatever is appropriate. By casting the Task<bool> to a Task you can hide that implementation from the public API.
That should also provide the "How" variations of the first two questions that you asked instead of the "can I" versions you asked. You can use a TaskCompletionSource to generate a task that is completed whenever you say it is, using whatever asynchronous construct you want, which may or may not involve the use of additional threads.

Categories

Resources