I have the following code snippet of a client and a grain in Orleans. (Although the recommended way to develop in Orleans is to await Tasks, the following code doesn't await at some points purely for experimentation purposes)
// client code
while(true)
{
Console.WriteLine("Client giving another request");
double temperature = random.NextDouble() * 40;
var grain = client.GetGrain<ITemperatureSensorGrain>(500);
Task t = sensor.SubmitTemperatureAsync((float)temperature);
Console.WriteLine("Client Task Status - "+t.Status);
await Task.Delay(5000);
}
// ITemperatureSensorGrain code
public async Task SubmitTemperatureAsync(float temperature)
{
long grainId = this.GetPrimaryKeyLong();
Console.WriteLine($"{grainId} outer received temperature: {temperature}");
Task x = SubmitTemp(temperature); // SubmitTemp() is another function in the same grain
x.Ignore();
Console.WriteLine($"{grainId} outer received temperature: {temperature} exiting");
}
public async Task SubmitTemp(float temp)
{
for(int i=0; i<1000; i++)
{
Console.WriteLine($"Internal function getting awaiting task {i}");
await Task.Delay(1000);
}
}
The output when I run the above code is the following:
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 23.79668
Internal function getting awaiting task 0
500 outer received temperature: 23.79668 exiting
Internal function getting awaiting task 1
Internal function getting awaiting task 2
Internal function getting awaiting task 3
Internal function getting awaiting task 4
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 39.0514
Internal function getting awaiting task 0 <------- from second call to SubmitTemp
500 outer received temperature: 39.0514 exiting
Internal function getting awaiting task 5 <------- from first call to SubmitTemp
Internal function getting awaiting task 1
Internal function getting awaiting task 6
Internal function getting awaiting task 2
Internal function getting awaiting task 7
Internal function getting awaiting task 3
Internal function getting awaiting task 8
Internal function getting awaiting task 4
Internal function getting awaiting task 9
The output makes sense from the perspective of a normal .Net application. If I can take help from this stackoverflow post, what is happening here is that:
Client makes a call to ITemperatureSendorGrain and proceeds ahead. When the await is hit, the client thread is returned to the threadpool.
SubmitTemperatureAsync receives the request and calls a local async function SubmitTemp.
SubmitTemp prints statement corresponding to i=0 after which point it hits await. Await causes the rest of the for loop to be scheduled as a continuation of the awaitable (Task.Delay) and the control returns to the calling function SubmitTemperatureAsync. Note here that the thread isn't returned to the threadpool when it encounters await in SubmitTemp function. The thread control is actually returned to the calling function SubmitTemperatureAsync. So, a turn, as defined in Orleans docs, ends when the top level method encounters an await. When a turn ends, the thread is returned to the threadpool.
The calling function doesn't wait for the task to finish and exits.
When the awaitable in SubmitTemp returns after 1s, it gets a thread from the threadpool and schedules the rest of the for loop on it.
When the awaitable in client code returns, it makes another call to the same grain and another round of for loop is scheduled corresponding to the second call to SubmitTemp.
My first question is have I correctly described what's happening in the code, especially about the thread not being returned to the thread pool when await is hit in the function SubmitTemp.
According to single-threaded nature of grains, at any time only one thread will be executing a grain's code. Also, once a request to a grain begins execution, it will be completed fully before the next request is taken up (called chunk based execution in the Orleans docs). On a high-level, this is true for the above code because the next call to SubmitTemperatureAsync will happen only when the current call to the method exits.
However, SubmitTemp was actually a sub-function of SubmitTemperatureAsync. Although SubmitTemperatureAsync exited, SubmitTemp is still executing and while it does that, Orleans allowed another call to SubmitTemperatureAsync to execute. Doesn't this violate the single-threaded nature of Orleans grain is my second question?
Consider that SubmitTemp in its for loop needs to access some data members of the grain class. So, the ExecutionContext will be captured when await is encountered and when Task.Delay(1000) returns, the captured ExecutionContext will be passed to the scheduling of the remainder for loop on a thread. Because ExecutionContext is passed, the remainder for loop will be able to access the data members in spite of running on a different thread. This is something which happens in any normal .Net asynchronous application.
My third question is about SynchronizationContext. I did a cursory search in the Orleans repository but couldn't find any implementation of SynchronizationContext.Post(), which leads me to believe that no SynchronizationContext is required to run Orleans methods. Can anyone confirm this? If this isn't true, and a SynchronizationContext is required, wouldn't the parallel execution of the various invocations of SubmitTemp (as shown in the above code), run the risk of ending in a deadlock (if someone holds on to the SynchronizationContext and doesn't release it)?
Question 1: Is the described execution flow an accurate representation of what's happening?
Your description looks roughly correct to me, but here's a few finer points:
Whether or not there's a thread pool is an implementation detail.
'Turns' are each synchronous portion of work scheduled on the activation's TaskScheduler.
Therefore, a turn ends whenever a execution has to be yielded back to the TaskScheduler.
That could be because an await was hit which wasn't synchronously completed, or maybe the user isn't using await at all and is programming using ContinueWith or custom awaitables.
A turn could be ended from a non-top-level method, eg if the code was changed to await SubmitTemp(x) instead of .Ignoring() it, then the turn would end when the Task.Delay(...) was hit inside SubmitTemp(x).
Question 2: Does the example program demonstrate a violation of the single-threadedness guarantee?
No, there is only ever a single thread executing the grain's code at a given time. However, that 'thread' has to split its time between the various tasks which are scheduled on the activation's TaskScheduler. i.e, there will never be a time where you suspend the process and discover that two threads are executing your grain's code at the same time.
As far as the runtime is concerned, the processing of your message ends when the Task (or other awaitable type) returned from the top level method completes. Until that occurs, no new messages will be scheduled for execution on your activation. Background tasks spawned from your methods are always allowed to interleave with other tasks.
.NET allows child tasks to be attached to their parent task. In that case, the parent task only completes when all child tasks complete. This is not the default behavior, however, and it's generally suggested that you avoid opting-in to this behavior (eg, by passing TaskCreationOptions.AttachedToParent to Task.Factory.StartNew).
If you did make use of that behavior (please don't), then you would see your activation loop on the first call to SubmitTemp() indefinitely, and no more messages will be processed.
Question 3: Does Orleans use SynchronizationContext?
Orleans does not use SynchronizationContext. Instead, it uses custom TaskScheduler implementations. See ActivationTaskScheduler.cs. Each activation has its own ActivationTaskScheduler and all messages are scheduler using that scheduler.
Regarding the follow-on question, the Task instances (each one representing a synchronous piece of work) which are scheduled against the activation are inserted into the same queue and so they are allowed to interleave, but the ActivationTaskScheduler is only executed by one thread at a time.
I understand that this is a contrived code snippet that was written to explore the execution guarantees of the Orleans runtime. I'm a little concerned that somebody might read this and misconstrued that this is a recommended pattern of how grain methods should be implemented.
That's why I'd like to stress that the recommended way of writing grain code is to await every Task in the call stack. In the above code it would mean awaiting x in the grain method and t in the client code. By default grains are non-reentrant, and that would prevent a second call from the client to start executing before execution of the first one is completed. Or one might choose to mark the grain class as [Reentrant] and allow interleaving of a second call. That would be much more clear and explicit than the background loop, and would make error handling possible.
Related
I'm learning C# with Andrew Troelsen's book "Pro C# 7 With .NET and .NET Core". On chapter 19 (async programming) the author used these sample codes:
static async Task Main(string[] args)
{
Console.WriteLine(" Fun With Async ===>");
string message = await DoWorkAsync();
Console.WriteLine(message);
Console.WriteLine("Completed");
Console.ReadLine();
}
static async Task<string> DoWorkAsync()
{
return await Task.Run(() =>
{
Thread.Sleep(5_000);
return "Done with work!";
});
}
The author then states
"... this keyword (await) will always modify a method that returns a Task object. When the flow of logic reaches the await token, the calling thread is suspended in this method until the call completes. If you were to run this version of the application, you would find that the Completed message shows before the Done with work! message. If this were a graphical application, the user could continue to use the UI while the DoWorkAsync() method executes".
But when I ran this code in VS I did not get this behavior. The Main thread actually gets blocked for 5 seconds and "Completed" doesn't show until after "Done with work!".
Looking through various online documentation and articles regarding how async/await works, I thought "await" would work such as when the first "await" is encountered, the program checks if the method has already completed, and if not, it would immediately "return" to the calling method, and then come back once the awaitable task completes.
But if the calling method is Main() itself, who does it return to? Would it simply wait for the await to complete? Is that why the code is behaving as it is (waiting for 5 seconds before printing "Completed")?
But this leads to the next question: because DoWorkAsync() itself here calls another await method, when that await Task.Run() line is encountered, which would obviously not complete till 5 seconds later, shouldn't DoWorkAsync() immediately return to the calling method Main(), and if that happens, shouldn't Main() proceed to print "Completed", as the book author suggested?
BTW, the book is for C# 7 but I'm running VS 2019 with C# 8, if that makes any difference.
I strongly recommend reading this blog post from 2012 when the await keyword was introduced, but it explains how asynchronous code works in console programs: https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/
The author then states
this keyword (await) will always modify a method that returns a Task object. When the flow of logic reaches the await token, the calling thread is suspended in this method until the call completes. If you were to run this version of the application, you would find that the "Completed" message shows before the "Done with work!" message. If this were a graphical application, the user could continue to use the UI while the DoWorkAsync() method executes".
The author is being imprecise.
I would change this:
When the flow of logic reaches the await token, the calling thread is suspended in this method until the call completes
To this:
When the flow of logic reaches the await token (which is after DoWorkAsync returns a Task object), the local state of the function is saved in-memory somewhere and the running thread executes a return back to the Async Scheduler (i.e. the thread pool).
My point is that await does not cause a thread to "suspend" (nor does it cause a thread to block either).
The next sentence is also a problem:
If you were to run this version of the application, you would find that the "Completed" message shows before the "Done with work!" message
(I assume by "this version" the author is referring to a version that's syntactically identical but omits the await keyword).
The claim being made is incorrect. The called method DoWorkAsync still returns a Task<String> which cannot be meaningfully passed to Console.WriteLine: the returned Task<String> must be awaited first.
Looking through various online documentation and articles regarding how async/await works, I thought "await" would work such as when the first "await" is encountered, the program checks if the method has already completed, and if not, it would immediately "return" to the calling method, and then come back once the awaitable task completes.
Your thinking is generally correct.
But if the calling method is Main() itself, who does it return to? Would it simply wait for the await to complete? Is that why the code is behaving as it is (waiting for 5 seconds before printing "Completed")?
It returns to the default Thread Pool maintained by the CLR. Every CLR program has a Thread Pool, which is why even the most trivial of .NET programs' processes will appear in Windows Task Manager with a thread-count between 4 and 10. The majority of those threads will be suspended, however (but the fact they're suspended is unrelated to the use of async/await.
But this leads to the next question: because DoWorkAsync() itself here calls another awaited method, when that await Task.Run() line is encountered, which would obviously not complete till 5 seconds later, shouldn't DoWorkAsync() immediately return to the calling method Main(), and if that happens, shouldn't Main() proceed to print "Completed", as the book author suggested?
Yes and no :)
It helps if you look at the raw CIL (MSIL) of your compiled program (await is a purely syntactic feature that does not depend on any substantial changes to the .NET CLR, which is why the async/await keywords were introduced with .NET Framework 4.5 even though the .NET Framework 4.5 runs on the same .NET 4.0 CLR which predates it by 3-4 years.
To start, I need to syntactically rearrange your program to this (this code looks different, but it compiles to identical CIL (MSIL) as your original program):
static async Task Main(string[] args)
{
Console.WriteLine(" Fun With Async ===>");
Task<String> messageTask = DoWorkAsync();
String message = await messageTask;
Console.WriteLine( message );
Console.WriteLine( "Completed" );
Console.ReadLine();
}
static async Task<string> DoWorkAsync()
{
Task<String> threadTask = Task.Run( BlockingJob );
String value = await threadTask;
return value;
}
static String BlockingJob()
{
Thread.Sleep( 5000 );
return "Done with work!";
}
Here's what happens:
The CLR loads your assembly and locates the Main entrypoint.
The CLR also populates the default thread-pool with threads it requests from the OS, it suspends those threads immediately (if the OS doesn't suspend them itself - I forget those details).
The CLR then chooses a thread to use as the Main thread and another thread as the GC thread (there's more details to this, I think it may even use the main OS-provided CLR entrypoint thread - I'm unsure of these details). We'll call this Thread0.
Thread0 then runs Console.WriteLine(" Fun With Async ===>"); as a normal method-call.
Thread0 then calls DoWorkAsync() also as a normal method-call.
Thread0 (inside DoWorkAsync) then calls Task.Run, passing a delegate (function-pointer) to BlockingJob.
Remember that Task.Run is shorthand for "schedule (not immediately-run) this delegate on a thread in the thread-pool as a conceptual "job", and immediately return a Task<T> to represent the status of that job".
For example, if the thread-pool is depleted or busy when Task.Run is called then BlockingJob won't run at all until a thread returns to the pool - or if you manually increase the size of the pool.
Thread0 is then immediately given a Task<String> that represents the lifetime and completion of BlockingJob. Note that at this point the BlockingJob method may or may not have run yet, as that's entirely up to your scheduler.
Thread0 then encounters the first await for BlockingJob's Job's Task<String>.
At this point actual CIL (MSIL) for DoWorkAsync contains an effective return statement which causes real execution to return to Main, where it then immediately returns to the thread-pool and lets the .NET async scheduler start worrying about scheduling.
This is where it gets complicated :)
So when Thread0 returns to the thread-pool, BlockingJob may or may not have been called depending on your computer setup and environment (things happen differently if your computer has only 1 CPU core, for example - but many other things too!).
It's entirely possible that Task.Run put the BlockingJob job into the scheduler and then not not actually run it until Thread0 itself returns to the thread-pool, and then the scheduler runs BlockingJob on Thread0 and the entire program only uses a single-thread.
But it's also possible that Task.Run will run BlockingJob immediately on another pool thread (and this is the likely case in this trivial program).
Now, assuming that Thread0 has yielded to the pool and Task.Run used a different thread in the thread-pool (Thread1) for BlockingJob, then Thread0 will be suspended because there are no other scheduled continuations (from await or ContinueWith) nor scheduled thread-pool jobs (from Task.Run or manual use of ThreadPool.QueueUserWorkItem).
(Remember that a suspended thread is not the same thing as a blocked thread! - See footnote 1)
So Thread1 is running BlockingJob and it sleeps (blocks) for those 5 seconds because Thread.Sleep blocks which is why you should always prefer Task.Delay in async code because it doesn't block!).
After those 5 seconds Thread1 then unblocks and returns "Done with work!" from that BlockingJob call - and it returns that value to Task.Run's internal scheduler's call-site and the scheduler marks the BlockingJob job as complete with "Done with work!" as the result value (this is represented by the Task<String>.Result value).
Thread1 then returns to the thread-pool.
The scheduler knows that there is an await that exists on that Task<String> inside DoWorkAsync that was used by Thread0 previously in step 8 when Thread0 returned to the pool.
So because that Task<String> is now completed, it picks out another thread from the thread-pool (which may or may not be Thread0 - it could be Thread1 or another different thread Thread2 - again, it depends on your program, your computer, etc - but most importantly it depends on the synchronization-context and if you used ConfigureAwait(true) or ConfigureAwait(false)).
In trivial console programs without a synchronization context (i.e. not WinForms, WPF, or ASP.NET (but not ASP.NET Core)) then the scheduler will use any thread in the pool (i.e. there's no thread affinity). Let's call this Thread2.
(I need to digress here to explain that while your async Task<String> DoWorkAsync method is a single method in your C# source code but internally the DoWorkAsync method is split-up into "sub-methods" at each await statement, and each "sub-method" can be entered into directly).
(They're not "sub-methods" but actually the entire method is rewritten into a hidden state-machine struct that captures local function state. See footnote 2).
So now the scheduler tells Thread2 to call into the DoWorkAsync "sub- method" that corresponds to the logic immediately after that await. In this case it's the String value = await threadTask; line.
Remember that the scheduler knows that the Task<String>.Result is "Done with work!", so it sets String value to that string.
The DoWorkAsync sub-method that Thread2 called-into then also returns that String value - but not to Main, but right back to the scheduler - and the scheduler then passes that string value back to the Task<String> for the await messageTask in Main and then picks another thread (or the same thread) to enter-into Main's sub-method that represents the code after await messageTask, and that thread then calls Console.WriteLine( message ); and the rest of the code in a normal fashion.
Footnotes
Footnote 1
Remember that a suspended thread is not the same thing as a blocked thread: This is an oversimplification, but for the purposes of this answer, a "suspended thread" has an empty call-stack and can be immediately put to work by the scheduler to do something useful, whereas a "blocked thread" has a populated call-stack and the scheduler cannot touch it or repurpose it unless-and-until it returns to the thread-pool - note that a thread can be "blocked" because it's busy running normal code (e.g. a while loop or spinlock), because it's blocked by a synchronization primitive such as a Semaphore.WaitOne, because it's sleeping by Thread.Sleep, or because a debugger instructed the OS to freeze the thread).
Footnote 2
In my answer, I said that the C# compiler would actually compile code around each await statement into "sub-methods" (actually a state-machine) and this is what allows a thread (any thread, regardless of its call-stack state) to "resume" a method where its thread returned to the thread-pool. This is how that works:
Supposing you have this async method:
async Task<String> FoobarAsync()
{
Task<Int32> task1 = GetInt32Async();
Int32 value1 = await task1;
Task<Double> task2 = GetDoubleAsync();
Double value2 = await task2;
String result = String.Format( "{0} {1}", value1, value2 );
return result;
}
The compiler will generate CIL (MSIL) that would conceptually correspond to this C# (i.e. if it were written without async and await keywords).
(This code omits lots of details like exception handling, the real values of state, it inlines AsyncTaskMethodBuilder, the capture of this, and so on - but those details aren't important right now)
Task<String> FoobarAsync()
{
FoobarAsyncState state = new FoobarAsyncState();
state.state = 1;
state.task = new Task<String>();
state.MoveNext();
return state.task;
}
struct FoobarAsyncState
{
// Async state:
public Int32 state;
public Task<String> task;
// Locals:
Task<Int32> task1;
Int32 value1
Task<Double> task2;
Double value2;
String result;
//
public void MoveNext()
{
switch( this.state )
{
case 1:
this.task1 = GetInt32Async();
this.state = 2;
// This call below is a method in the `AsyncTaskMethodBuilder` which essentially instructs the scheduler to call this `FoobarAsyncState.MoveNext()` when `this.task1` completes.
// When `FoobarAsyncState.MoveNext()` is next called, the `case 2:` block will be executed because `this.state = 2` was assigned above.
AwaitUnsafeOnCompleted( this.task1.GetAwaiter(), this );
// Then immediately return to the caller (which will always be `FoobarAsync`).
return;
case 2:
this.value1 = this.task1.Result; // This doesn't block because `this.task1` will be completed.
this.task2 = GetDoubleAsync();
this.state = 3;
AwaitUnsafeOnCompleted( this.task2.GetAwaiter(), this );
// Then immediately return to the caller, which is most likely the thread-pool scheduler.
return;
case 3:
this.value2 = this.task2.Result; // This doesn't block because `this.task2` will be completed.
this.result = String.Format( "{0} {1}", value1, value2 );
// Set the .Result of this async method's Task<String>:
this.task.TrySetResult( this.result );
// `Task.TrySetResult` is an `internal` method that's actually called by `AsyncTaskMethodBuilder.SetResult`
// ...and it also causes any continuations on `this.task` to be executed as well...
// ...so this `return` statement below might not be called until a very long time after `TrySetResult` is called, depending on the contination chain for `this.task`!
return;
}
}
}
Note that FoobarAsyncState is a struct rather than a class for performance reasons that I won't get into.
When you use the static async Task Main(string[] args) signature, the C# compiler generates behind the scenes a MainAsync method, and the actual Main method is rewritten like this:
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
// Main body here
}
This means that the main thread of the console application, the thread having ManagedThreadId equal to 1, becomes blocked immediately after the first await of a non-completed task is hit, and remains blocked for the entire lifetime of the application! After that point the application runs exclusively on ThreadPool threads (unless your code starts threads explicitly).
This is a waste of a thread, but the alternative is to install a SynchronizationContext to the Console application, which has other drawbacks:
The application becomes susceptible to the same deadlock scenarios that plague the UI applications (Windows Forms, WPF etc).
There is nothing built-in available, so you must search for third-party solutions. Like Stephen Cleary's AsyncContext from the Nito.AsyncEx.Context package.
So the price of 1 MB of wasted RAM becomes a bargain, when you consider the complexity of the alternative!
There is another alternative though, which makes better use of the main thread. And this is to avoid the async Task Main signature. Just use the .GetAwaiter().GetResult(); after every major asynchronous method of your app. This way after the method completes you'll be back in the main thread!
static void Main(string[] args)
{
Console.WriteLine(" Fun With Async ===>");
string message = DoWorkAsync().GetAwaiter().GetResult();
Console.WriteLine(message);
Console.WriteLine($"Completed, Thread: {Thread.CurrentThread.ManagedThreadId}");
Console.ReadLine();
}
In this code:
public async Task v_task()
{
await Task.Run(() => Console.WriteLine("Hello!"));
}
public async void v1()
{
await v_task();
// some other actions...
}
public void ButtonClick()
{
v1();
Console.WriteLine("Hi!");
}
Which methods above are actually executed in parallel in the async/await generated lower thread pool if ButtonClick is called?
I mean, what should be my concerns about race conditions working with async/await? All async methods are mandatory executed in the same caller's thread? Should I use mutex on possible shared state? If yes, how could I detect what are the shared state objects?
Which methods above are actually executed in parallel in the async/await generated lower thread pool if ButtonClick is called?
Only the Console.WriteLine within the Task.Run.
I mean, what should be my concerns about race conditions working with async/await?
I suggest you start by reading my async intro, which explains how await actually works.
In summary, async methods are usually written in a serially asynchronous fashion. Take this code for example:
CodeBeforeAwait();
await SomeOtherMethodAsync();
CodeAfterAwait();
You can always say that CodeBeforeAwait will execute to completion first, then SomeOtherMethodAsync will be called. Then our method will (asynchronously) wait for SomeOtherMethodAsync to complete, and only after that will CodeAfterAwait be called.
So it's serially asynchronous. It executes in a serial fashion, just like you'd expect it to, but also with an asynchronous point in that flow (the await).
Now, you can't say that CodeBeforeAwait and CodeAfterAwait will execute within the same thread, at least not without more context. await by default will resume in the current SynchronizationContext (or the current TaskScheduler if there is no SyncCtx). So, if the sample method above was executed in the UI thread, then you would know that CodeBeforeAwait and CodeAfterAwait will both execute on the UI thread. However, if it was executed without a context (i.e., from a background thread or Console main thread), then CodeAfterAwait may run on a different thread.
Note that even if parts of a method run on a different thread, the runtime takes care of putting any barriers in place before continuing the method, so there's no need to barrier around variable access.
Also note that your original example uses Task.Run, which explicitly places work on the thread pool. That's quite different than async/await, and you will definitely have to treat that as multithreaded.
Should I use mutex on possible shared state?
Yes. For example, if your code uses Task.Run, then you'll need to treat that as a separate thread. (Note that with await, it's a lot easier to not share state at all with other threads - if you can keep your background tasks pure, they're much easier to work with).
If yes, how could I detect what are the shared state objects?
Same answer as with any other kind of multi-threaded code: code inspection.
If you call an async function, your thread will perform this function until it reaches an await.
If you weren't using async-await, the thread would yield processing until the awaited code was finished and continue with the statement after the await.
But as you are using async-await, you told the compiler that whenever the thread has to wait for something, you have some other things it can do instead of waiting, The thread will do those other things until you say: now await until your original thing is finished.
Because of the call to an async function we are certain that somewhere inside there should be an await. Note that if you call an async function that doesn't await you get a compiler warning that the function will run synchronously.
Example:
private async void OnButton1_clickec(object sender, ...)
{
string dataToSave = ...;
var saveTask = this.MyOpenFile.SaveAsync(dataToSave);
// the thread will go into the SaveAsync function and will
// do all things until it sees an await.
// because of the async SaveAsync we know there is somewhere an await
// As you didn't say await this.MyOpenfile.SaveAsync
// the thread will not wait but continue with the following
// statements:
DoSomethingElse()
await saveTask;
// here we see the await. The thread was doing something else,
// finished it, and now we say: await. That means it waits until its
// internal await is finished and continues with the statements after
// this internal await.
Note that even if the await somewhere inside SaveAsync was finished, the thread will not perform the next statement until you await SaveTask. This has the effect that DoSomethingElse will not be interrupted if the await inside the SaveAsync was finished.
Therefore normally it's not useful to create an async function that does not return either a Task or a Task < TResult >
The only exception to this is an event handler. The GUI doesn't have to wait until your event handler is finished.
Background:
I have a "Messenger" class. It sends messages. But due to limitations, let's say it can only send - at most - 5 messages at a time.
I have a WPF application which queues messages as needed, and waits for the queued message to be handled before continuing. Due to the asynchronous nature of the application, any number of messages could be awaited at any given time.
Current Implementation:
To accomplish this, I've implemented a Task<Result> SendMessage(Message message) API within my messaging class. Internal to the messaging class is a custom TaskScheduler (the LimitedConcurrencyTaskScheduler from MSDN), with its concurrency level set to 5. In this way, I would expect that no matter how many messages are queued, only 5 will be sent out at a time, and my client application will patiently wait until its respective message has been handled.
Problem:
When I await the SendMessage method, I can see via the debugger that the message was completed and the result returned, but my code never executes beyond the awaited method call!
Is there some special considerations that need to be made, when awaiting a Task which was scheduled using a different TaskScheduler?
Snipped Code:
From my client/consuming function:
public async Task Frobulate()
{
Message myMessage = new Message(x, y, z);
await messenger.SendMessage(myMessage);
//Code down here never executes!
}
From my messenger class:
private TaskScheduler _messengerTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(5);
private TaskFactory _messengerTaskFactory = new TaskFactory(_messengerScheduler);
public Task<Result> SendMessage(Message message)
{
//My debugger has verified that "InternalSendMessage" has completed,
//but the caller's continuation appears to never execute
return _messengerTaskFactory.StartNew(() => InternalSendMessage(message));
}
Update:
The 'freeze' does not actually appear to be caused by my custom TaskScheduler; when I queue up the Task with the default TaskFactory, the same behavior occurs! There must be something else happening at a more fundamental level, likely due to my own stupidity.
Based on the comments, you probably have a deadlock because you're blocking on async code.
When using async, whenever there are thread restrictions on the SynchronizationContext or TaskScheduler and the code blocks using Task.Result or Task.Wait there's a possibility of deadlocking. The asynchronous operation needs a thread to finish execution, which it can't get because the SynchronizationContext (or TaskScheduler in your case) is waiting for that same exact operation to complete before allowing "new" ones to run.
Go deeper in Stephen Cleary's blog post: Don't Block on Async Code
I know there are a lot of questions about async/await, but I couldn't find any answer to this.
I've encountered something I don't understand, consider the following code:
void Main()
{
Poetry();
while (true)
{
Console.WriteLine("Outside, within Main.");
Thread.Sleep(200);
}
}
async void Poetry()
{
//.. stuff happens before await
await Task.Delay(10);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Inside, after await.");
Thread.Sleep(200);
}
}
Obviously, on the await operator, the control returns to the caller, while the method being awaited, is running on the background. (assume an IO operation)
But after the control comes back to the await operator, the execution becomes parallel, instead of (my expectation) remaining single-threaded.
I'd expect that after "Delay" has been finished the thread will be forced back into the Poetry method, continues from where it left.
Which it does. The weird thing for me, is why the "Main" method keeps running? is that one thread jumping from one to the other? or are there two parallel threads?
Isn't it a thread-safety problem, once again?
I find this confusing. I'm not an expert. Thanks.
I have a description on my blog of how async methods resume after an await. In essence, await captures the current SynchronizationContext unless it is null in which case it captures the current TaskScheduler. That "context" is then used to schedule the remainder of the method.
Since you're executing a Console app, there is no SynchronizationContext, and the default TaskScheduler is captured to execute the remainder of the async method. That context queues the async method to the thread pool. It is not possible to return to the main thread of a Console app unless you actually give it a main loop with a SynchronizationContext (or TaskScheduler) that queues to that main loop.
Read It's All About the SynchronizationContext and I'm sure it'll become less confusing. The behavior you're seeing makes perfect sense. Task.Delay uses Win32 Kernel Timer APIs internally (namely, CreateTimerQueueTimer). The timer callback is invoked on a pool thread, different from your Main thread. That's where the rest of Poetry after await continues executing. This is how the default task scheduler works, in the absence of synchronization context on the original thread which initiated the await.
Because you don't do await the Poetry() task (and you can't unless you return Task instead of void), its for loop continues executing in parallel with the while loop in your Main. Why, and more importantly, how would you expect it to be "forced" back onto the Main thread? There has to be some explicit point of synchronization for this to happen, the thread cannot simply get interrupted in the middle of the while loop.
In a UI application, the core message loop may serve as such kind of synchronization point. E.g. for a WinForms app, WindowsFormsSynchronizationContext would make this happen. If await Task.Delay() is called on the main UI thread, the code after await would asynchronously continue on the main UI thread, upon some future iteration of the message loop run by Application.Run.
So, if it was a UI thread, the rest of the Poetry wouldn't get executed in parallel with the while loop following the Poetry() call. Rather, it would be executed when the control flow had returned to the message loop. Or, you might explicitly pump messages with Application.DoEvents() for the continuation to happen, although I wouldn't recommend doing that.
On a side note, don't use async void, rather use async Task, more info.
When you call an async routine the purpose of this is to allow the program to run a method while still allowing the calling routine, form or application to continue to respond to user input (in other words, continue execution normally). The "await" keyword pauses execution at the point it is used, runs the task using another thread then returns to that line when the thread completes.
So, in your case if you want the main routine to pause until the "Poetry" routine is done you need to use the await keyword something like this:
void async Main()
{
await Poetry();
while (true)
{
Console.WriteLine("Outside, within Main.");
Thread.Sleep(200);
}
}
You will also need to change the definition for Poetry to allow the await keyword to be used:
async Task Poetry()
Because this question really intrigued me I went ahead and wrote an example program you can actually compile. Just create a new console application and paste this example in. You can see the result of using "await" versus not using it.
class Program
{
static void Main(string[] args)
{
RunMain();
// pause long enough for all async routines to complete (10 minutes)
System.Threading.Thread.Sleep(10 * 60 * 1000);
}
private static async void RunMain()
{
// with await this will pause for poetry
await Poetry();
// without await this just runs
// Poetry();
for (int main = 0; main < 25; main++)
{
System.Threading.Thread.Sleep(10);
Console.WriteLine("MAIN [" + main + "]");
}
}
private static async Task Poetry()
{
await Task.Delay(10);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("IN THE POETRY ROUTINE [" + i + "]");
System.Threading.Thread.Sleep(10);
}
}
}
Happy testing! Oh, and you can still read more information here.
I'd like to answer my own question here.
Some of you gave me great answers which all helped me understand better (and were thumbed up). Possibly no one gave me a full answer because I've failed to ask the full question. In any case someone will encounter my exact misunderstanding, I'd like this to be the first answer (but I'll recommend to look at some more answers below).
So, Task.Delay uses a Timer which uses the operating system to fire an event after N milliseconds. after this period a new pooled thread is created, which basically does almost nothing.
The await keyword means that after the thread has finished (and it's doing almost nothing) it should continue to whatever comes after the await keyword.
Here comes the synchronization context, as mentioned in other answers.
If there is no such context, the same newly-created-pooled-thread will continue running what ever comes after the await.
If there is a synchronizing context, the newly-created-pool-thread, will only push whatever comes after the await, into synchronizing context.
For the sake of it, here are a few points I didn't realize:
The async/await are not doing anything which wasn't (technologly speaking) possible before. Just maybe amazingly clumsy.
It's is just a language support for some of .NET 4.5 classes.
It's much like yield return. It may break your method into a few methods, and may even generate a class behind, and use some methods from the BCL, but nothing more.
Anyway, I recommend reading C# 5.0 In A Nutshell's chapter "Concurrency and Asynchrony". It helped me a lot. It is great, and actually explains the whole story.
After playing with .NET 4.5 async\await framework I have a question.
Let's look at this program (the msdn example):
async Task<int> AccessTheWebAsync()
{
HttpClient client = new HttpClient();
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
DoIndependentWork();
string urlContents = await getStringTask;
return urlContents.Length;
}
void DoIndependentWork()
{
resultsTextBox.Text += "Working . . . . . . .\r\n";
}
The program will run in this order:
new HttpClient.
GetStringAsync invoke synchronously.
In some point, GetStringAsync calls await and the control return to AccessTheWebAsync.
DoIndependentWork invoked.
The program waits the string to be returned (blocks if the operation didn't complete).
return the length of the urlContent.
One thing that took me a while to understand is that the method GetStringAsync runs synchronously despite its name (the name convention is really misleading).
In oreder to run the method asynchronously, we need to use Task.Run or Task.Factory.StartNew explicitly.
But the real question is, if we have independent work, why not do it right away rather then waiting the await to be called from GetStringAsync? (In other words, why async methods doesn't run asynchronously by definition?)
EDIT:
I'll rephrase the second and third operations:
(2) GetStringAsync start synchronously.
(3) In some point, GetStringAsync calls await and the thread forks, the control return to AccessTheWebAsync.
One thing that took me a while to understand is that the method GetStringAsync runs synchronously despite its name (the name convention is really misleading).
That is incorrect. GetStringAsync returns a Task<string>. It will return immediately, which means DoIndependentWork will (potentially) run before the download has completed. The await operator will asynchronously wait until the Task<T> returned by GetStringAsync is done.
But the real question is, if we have independent work, why not do it right away rather then waiting the await to be called from GetStringAsync? (In other words, why async methods doesn't run asynchronously by definition?)
The actual "work" of the asynchronous methods is running after the method has returned. The Task that's returned will (normally) be in a state that's not completed, which means the method is still running asynchronously. You can check Task.IsCompleted to verify.
Try this:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true
Each async method is split into segments on each await. Each segment will become a state on a compiler generated state machine.
Each await instruction works on an awaitable for which a Task is the most common case.
Each state/segment will be executed synchronously until the where the received awaitable is checked if it is already completed.
If the awaitable is completed, it execution will continue on the next state.
If the awaitable is not completed and there isn't a current SynchronizationContext, the execution will be blocked until the the awaitable is completed at which time the execution of the next state will be started.
If a current SynchronizationContext exists, the execution will return to the caller and when the awaitable is completed the continuation to the next state will be posted to the captured SynchronizationContext.
The program waits the string to be returned (blocks if the operation
didn't complete).
await doesn't block on await getStringTask. It will save the internal state of the AccessTheWebAsync method (local variables and where the execution point is) in a compiler-generated state machine object and will return to the outer method which called AccessTheWebAsync. The state will be restored and the execution will be resumed later asynchronously when getStringTask task has become completed, via a compiler-generated continuation callback. If you're familiar with C# iterators and yield keyword, the await state machine control flow is very similar to it, albeit iterators are executed synchronously.
How exactly the execution will be resumed after await depends on the synchronization context of the thread initiating the await. If it's a UI thread pumping Windows messages, the continuation callback will likely be called on the next iteration of the thread's message loop (typically inside Application.Run). If it's a non-UI thread (e.g., a console application), the continuation may happen on a different thread at all. So, while the control flow of your method remains logically linear, technically it is not (as it would have been if you just did getStringTask.Wait() instead of await getStringTask).
The program waits the string to be returned (blocks if the operation didn't complete).
No, it doesn't. await waits, but it doesn't block any threads. That's the whole point.
One thing that took me a while to understand is that the method GetStringAsync runs synchronously despite its name (the name convention is really misleading).
That's wrong. Most of the method does run asynchronously. It most likely also has a small synchronous part at the start, but that should be negligible.
In oreder to run the method asynchronously, we need to use Task.Run or Task.Factory.StartNew explicitly.
No, the method already does run asynchronously. If you want to run the small synchronous part on another thread, then you can use Task.Run(), but that almost never makes any sense. Also, don't use StartNew() for this, it doesn't work well with async methods.
In other words, why async methods doesn't run asynchronously by definition?
A significant point about await is that it resumes on the same context where it left.
This can be very useful in GUI applications, where you often want to modify the UI, then execute the async operation and then modify the UI again. If async automatically meant that the whole method executes on a ThreadPool thread, then that wouldn't be possible.
Also, doing it this way is more efficient, because you don't have to switch to another thread for something that takes very little time.