Async processing in asp.net core and kestrel thread pool
<p>I'm new to ASP.NET Core and to C# in general coming from the Java world and I'm a bit confused about how async/await keywords work: <a href="https://blogs.msdn.microsoft.com/ericlippert/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await/" rel="noreferrer">This article</a> explains that if a method is marked as async it means that</p>

<blockquote>
  <p>"this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” <strong>The whole point of async methods it that you stay on the current thread as much as possible</strong></p>
</blockquote>

<p>So I thought that it is a very cool way of passing control between an async method and its caller running on the <strong>same</strong> thread implemented on byte-code/virtual machine level. I mean I expected that the below code</p>

<pre><code>public static void Main(string[] args)
{
    int delay = 10;
    var task = doSthAsync(delay);
    System.Console.WriteLine("main 1: " + Thread.CurrentThread.ManagedThreadId);
    // some synchronous processing longer than delay:
    Thread.Sleep(2 * delay)
    System.Console.WriteLine("main 2: " + Thread.CurrentThread.ManagedThreadId);
    task.Wait();
}

public static async Task&lt;String&gt; doSthAsync(int delay)
{
    System.Console.WriteLine("async 1: " + Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(delay);
    System.Console.WriteLine("async 2: " + Thread.CurrentThread.ManagedThreadId);
    return "done";
}
</code></pre>

<p>would write</p>

<pre>
async 1: 1
main 1: 1
main 2: 1
async 2: 1
</pre>

<p>by passing control from <code>doSthAsync</code> to <code>Main</code> with <code>await</code> keyword and then back to <code>doSthAsync</code> with <code>task.Wait()</code> (and all of this happening on a single thread).</p>

<p>However the code above in fact prints</p>

<pre>
async 1: 1
main 1: 1
async 2: 4
main 2: 1
</pre>

<p>which means that it is merely a way to delegate an "async" method to a separate thread if the current thread is busy, which is almost exactly <strong>opposite</strong> to what the mentioned article states:</p>

<blockquote>
  <p>The “async” modifier on the method does not mean “this method is automatically scheduled to run on a worker thread asynchronously”</p>
</blockquote>

<p>So since "continuations" of async methods apparently <strong>are</strong> or at least <strong>may be</strong> scheduled to run on a different thread, I have some questions that seem fundamental for C# apps scalability:</p>

<p>Does each async method may run on a newly spawned thread or is the C# runtime maintaining a special thread-pool for this purpose and in the latter case how do I control the size of this pool?
<strong>UPDATE:</strong> thanks to @Servy I now know that in case of CMD-line apps it will be a thread-pool thread, but I still don't know how to control the number of these thread-pool threads).</p>

<p>How about in case of ASP.NET Core: how can I control the size of a thread-pool that Kestrel uses to perform async entity framework operations or other (network) I/O? Is it the same thread-pool it uses to accept HTTP requests?<br>
<strong>UPDATE:</strong> thanks to <a href="http://blog.stephencleary.com/2012/02/async-and-await.html" rel="noreferrer">this</a> and <a href="http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html" rel="noreferrer">this article</a> by Stephen Cleary I now understand that it will run "kind-of-single-threadly" by default, ie:, at any given time there will be exactly one thread within the SynchronizationContext of a given HTTP request, but this thread may be switched to another when some async operation is marked as finished and task is resumed. I also learned that it is possible to dispatch any async method "out of a given SynchronizationContext" to a thread-pool thread by using <code>ConfigureAwait(false)</code>. Still, I don't know how to control the number of thread-pool threads and if it is the same pool that is used by Kestrel to accept HTTP requests.</p>

<p>As I understand if all I/O is done asynchronously properly then the the size of these pools (whether it's just 1 thread-pool in fact or 2 separate) does not have anything to do with the number of outgoing connections being opened to external resources like DB, right? (threads running an asynchronous code will be continuously accepting new HTTP requests and as a result of their processing will be opening outgoing connections (to DB or to some web server to fetch some resource from the web) as fast as they can without any blocking)<br>
This in turn means that if I don't want to kill my DB I should set a sane limit of its connection pool, and make sure that awaiting for an available connection is also done asynchronously, right?<br>
Assuming that I'm correct I would still want to be able to limit the number of thread-pool threads in case some long lasting I/O operation is mistakenly done synchronously to prevent large number of threads being spawned as a result of large numbers of threads being blocked on this synchronous operation (ie: I think it's better to limit performance of my app rather than make it spawn insane number of threads due to such bug)</p>

<p>I also have "just out of curiosity" question: why actually async/await is not implemented to execute async tasks on the same thread? I don't know how to implement it on windows but on Unix this could be implemented using either select/poll system calls or signals, so I believe that there's a way to achieve this on windows as well and it would be a really cool language feature indeed.<br>
<strong>UPDATE:</strong> if I understand correctly this is <em>almost</em> how things will work if my SynchronizationContext is not null: ie code will run "kind-of-single-threadly": at any given time there will be exactly one thread within a given SynchronizationContext. However the way it is implemented will make a synchronous method waiting for async task to deadlock, right? (runtime will schedule marking of the task as finished to run on the same thread that is waiting for this task to be finished)</p>

<p>Thanks!</p>