Async/Await - is it *concurrent*?
It is concurrent, in the sense that many outstanding asychronous operations may be in progress at any time. It may or may not be multithreaded.
By default, await
will schedule the continuation back to the "current execution context". The "current execution context" is defined as SynchronizationContext.Current
if it is non-null
, or TaskScheduler.Current
if there's no SynchronizationContext
.
You can override this default behavior by calling ConfigureAwait
and passing false
for the continueOnCapturedContext
parameter. In that case, the continuation will not be scheduled back to that execution context. This usually means it will be run on a threadpool thread.
Unless you're writing library code, the default behavior is exactly what's desired. WinForms, WPF, and Silverlight (i.e., all the UI frameworks) supply a SynchronizationContext
, so the continuation executes on the UI thread (and can safely access UI objects). ASP.NET also supplies a SynchronizationContext
that ensures the continuation executes in the correct request context.
Other threads (including threadpool threads, Thread
, and BackgroundWorker
) do not supply a SynchronizationContext
. So Console apps and Win32 services by default do not have a SynchronizationContext
at all. In this situation, continuations execute on threadpool threads. This is why Console app demos using await
/async
include a call to Console.ReadLine
/ReadKey
or do a blocking Wait
on a Task
.
If you find yourself needing a SynchronizationContext
, you can use AsyncContext
from my Nito.AsyncEx library; it basically just provides an async
-compatible "main loop" with a SynchronizationContext
. I find it useful for Console apps and unit tests (VS2012 now has built-in support for async Task
unit tests).
For more information about SynchronizationContext
, see my Feb MSDN article.
At no time is DoEvents
or an equivalent called; rather, control flow returns all the way out, and the continuation (the rest of the function) is scheduled to be run later. This is a much cleaner solution because it doesn't cause reentrancy issues like you would have if DoEvents
was used.
Are swift actors concurrent, but not parallel?
In answer to your question, independent actor instances can run in parallel (as long as the cooperative thread pool is not constrained). When I ran your code snippets, I enjoyed parallel execution, so I suspect that there is some other aspect of your tests that yielded the serial behavior. We need a MCVE to diagnose this further.
For example, if you run this on an iOS simulator, that has an artificially constrained cooperative thread pool. See Maximum number of threads with async-await task groups. While I have been unable to manifest the behavior you describe, the simulator’s constrained cooperative thread pool can easily lead one to incorrect inferences about the potential parallelism.
If you ran this on a simulator, try testing this on a physical device or a macOS target, and you will enjoy a cooperative thread pool that will avail itself of all of the cores available on your device.
Promises vs async await behaviour javascript. Concurrency mode
To answer your questions, let me first try to explain how your code is working.
Understanding how your code works
Following steps explain the execution of your code:
Script execution start
Call
disallowConcurrency
function, passing in theinitCmp
as an argument.cmpConcurrentFunction
is assigned the return value ofdisallowConcurrency
functionCall
cmpConcurrentFunction
for the first time, passing in'I am called 1 second later'
as an argument. During this invocation,inprogressPromise
is a resolved promise returned byPromise.resolve()
. Awaiting it pauses the function execution.Call
cmpConcurrentFunction
for the second time, passing in'I am called 2 seconds later'
as an argument. During this second invocation,inprogressPromise
is still a resolved promise returned byPromise.resolve()
. Awaiting it pauses the function execution.Synchronous execution of script ends here. Event loop can now start processing the micro-task queue
Function paused as a result of first invocation of
cmpConcurrentFunction
is resumed, callingthen()
method on the promise returned byPromise.resolve()
. The value of theinprogressPromise
is updated by assigning it a new promise returned byinprogressPromise.then(...)
Function paused as a result of second invocation of
cmpConcurrentFunction
is resumed. From step 6, we know thatinprogressPromise
is now a promise returned byinprogressPromise.then(...)
. So, callingthen()
on it, we are simply creating a promise chain, adding a newthen()
method call at the end of the promise chain created in step 6.At this point, we have a chain that looks like this:
inProgressPromise
.then(() => fn('I am called 1 second later'))
.then(() => fn('I am called 2 seconds later'))Callback function of the first
then
method is called, which in turn calls thefn
argument, which isinitCmp
function. CallinginitCmp
sets up a timer that resolves the promiseinitCmp
returns after 1 second. When the promise is resolved after 1 second,'I am called 1 second later'
is logged on the console.When the promise returned by the first call to
initComp
function in the callback function of firstthen
method, is resolved, it resolves the promise returned by the firstthen
method. This leads to the invocation of the callback function of the secondthen
method. This again calls theinitComp
function, which then returns a new promise that is resolved after another 1 second.
This explains why you see 'I am called 2 seconds later'
logged on the console after 2 seconds.
Now answering your questions:
But i don't understand why this keeps working if i remove the await
inprogressPromise
await inprogressPromise
serves no purpose in your code other than pausing the calls to cmpConcurrentFunction
function. Both calls await the same promise that is returned by Promise.resolve()
.
So the await is not necessary? Why?
Because the output you see on the console is not because of await
, but because of the promise chain (step 7 above) that is constructed as a result of two invocations of the cmpConcurrentFunction
function.
Further more i was expecting this to work :
const disallowConcurrency = (fn) => {
let inprogressPromise = Promise.resolve();
return async (...args) => {
await inprogressPromise;
inprogressPromise = fn(...args);
};
};
The above code doesn't works as your original code because now the promise chain isn't constructed which is key to the output your original code produces.
With this implementation of disallowConcurrency
, your code is executed as explained below:
Call
cmpConcurrentFunction
function, passing in'I am called 1 second later'
as an argumentAwaiting
inprogressPromise
pauses the function executionCall
cmpConcurrentFunction
for the second time, passing in'I am called 2 seconds later'
as an argumentAwaiting
inprogressPromise
pauses the function execution. At this point, both invocations ofcmpConcurrentFunction
are paused and both function invocations are awaiting the same promise that was created as a result of callingPromise.resolve()
.This is the key point here: both invocations of
cmpConcurrentFunction
function are awaiting the same promise that was created byPromise.resolve()
. Why are both invocations awaiting the same promise? Because thecmpConcurrentFunction
is invoked for the second time before the first invocation ofcmpConcurrentFunction
is resumed and calls theinitComp
functionResume the first invocation of
cmpConcurrentFunction
, call theinitComp
function and assign its return value toinprogressPromise
.Calling
initComp
function sets up a timer that resolves the promiseinitComp
function returns after 1 secondResume the second invocation of
cmpConcurrentFunction
, call theinitComp
function and assign its return value toinprogressPromise
, overwriting the current value ofinprogressPromise
which is the promise returned by the first call toinitComp
function.Calling
initComp
function sets up a timer that resolves the promiseinitComp
function returns after 1 second
At this point, initComp
function has been invoked twice, setting up two separate timers that resolve their respective promise after 1 second each.
To summarize the differences between the original implementation of disallowConcurrency
function and this one is that in the original implementation, you have a promise chain which resolves the promises sequentially whereas in the second implementation, you have two separate promises (that don't depend on each other) and they are resolved after 1 second each.
Call async/await functions in parallel
You can await on Promise.all()
:
await Promise.all([someCall(), anotherCall()]);
To store the results:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Note that Promise.all
fails fast, which means that as soon as one of the promises supplied to it rejects, then the entire thing rejects.
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.all([happy('happy', 100), sad('sad', 50)])
.then(console.log).catch(console.log) // 'sad'
How to constrain concurrency (like `maxConcurrentOperationCount`) with Swift Concurrency?
One can insert a group.next()
call inside the loop after reaching a certain count, e.g.:
func download() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
for i in 0..<20 {
let source = sourceUrl(for: i)
let destination = destinationUrl(for: i)
if i >= 6 { // max of six at a time
try await group.next()
}
group.addTask {
let (url, _) = try await self.session.download(from: source)
try? FileManager.default.removeItem(at: destination)
try FileManager.default.moveItem(at: url, to: destination)
}
}
try await group.waitForAll()
}
}
That results in no more than six at a time:
Concurrent start with async/await
The issues is that your Promise callbacks are invoked immediately after you declare them.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
At this point, both callbacks have been invoked. 10000ms from this point, promise1
will resolve. 15000ms from this point, promise2
will resolve.
async function asyncTest() {
console.time();
const p1 = await promise1;
if (p1 === 'what') {
var p2 = await promise2;
}
console.log(p1, p2);
console.timeEnd();
}
asyncTest()
At this point, as both callbacks have already been invoked, both will be resolved within 15000ms, which is what you observed.
In order to stretch this to the 25000ms you were expecting, try rewriting the Promises as follows:
const promise1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
const promise2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
Note these are now function expressions that will not be invoked immediately. Then rewrite your asyncTest
function to invoke these expressions instead of simply await the already executing Promises:
async function asyncTest() {
console.time();
const p1 = await promise1();
if (p1 === 'what') {
var p2 = await promise2();
}
console.log(p1, p2);
console.timeEnd();
}
This will force promise2
to wait until promise1
is completed before beginning execution.
Concurrent async/await with sleep
Yes, it still applies. It fundamentally has to be that way because, like the linked answer says, each async function will be running on the same thread - std::thread::sleep
knows nothing about async, and so will make the whole thread sleep.
Nodejs (and JavaScript in general) is much more designed around async, so the language primitives and the language runtime are more async-aware in that way.
Concurrent execution of async methods
How would I go about letting them execute in parallel and join the results as they complete?
The simplest approach is just to create all the tasks and then await them:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var result1 = await task1;
var result1 = await task2;
var result1 = await task3;
You might also consider Task.WhenAll
. You need to consider the possibility that more than one task will fail... with the above code you wouldn't observe the failure of task3
for example, if task2
fails - because your async method will propagate the exception from task2
before you await task3
.
I'm not suggesting a particular strategy here, because it will depend on your exact scenario. You may only care about success/failure and logging one cause of failure, in which case the above code is fine. Otherwise, you could potentially attach continuations to the original tasks to log all exceptions, for example.
Related Topics
Find Image Format Using Bitmap Object in C#
Determine Client's Computer Name
How to Use C# 6 with Web Site Project Type
How to Loop Over the Properties of a Class
Ftp Directory Partial Listing with Wildcards
Why Are Tolookup and Groupby Different
Get All Properties Which Marked Certain Attribute
Allow Access Permission to Write in Program Files of Windows 7
Syncing SQL Server 2008 Databases Over Http Using Wcf & Sync Framework
How to Use Font Awesome Icons in Project as an Icon of Imagebutton
How to Derive Xml Element Name from an Attribute Value of a Class Using Annotations
Why Would One Ever Use the "In" Parameter Modifier in C#
Why I'm Getting Cs1012: "Too Many Characters in Character Literal" and Cs0019
In .Net 4.0, How to 'Sandbox' an In-Memory Assembly and Execute a Method
Wpf Webbrowser Mouse Events Not Working as Expected
Is Graphics.Drawimage Too Slow for Bigger Images
Wait for Response from the Serial Port and Then Send Next Data