Can anyone explain (or have a resource that explains) exactly when ThreadPool threads are released back to the ThreadPool? Here is a small example program (Dotnet fiddle: https://dotnetfiddle.net/XRso3q)
public static async Task Main()
{
Console.WriteLine("Start");
var t1 = ShortWork("SW1");
var t2 = ShortWork("SW2");
await Task.Delay(50);
var t3 = LongWork("LW1");
Console.WriteLine($"After starting LongWork Thread={Thread.CurrentThread.ManagedThreadId}");
await Task.WhenAll(t1, t2);
await t3;
Console.WriteLine("Done");
}
public static async Task ShortWork(string name)
{
Console.WriteLine($"SHORT Start {name} Thread={Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(500);
Console.WriteLine($"SHORT End {name} Thread={Thread.CurrentThread.ManagedThreadId}");
}
public static async Task LongWork(string name)
{
Console.WriteLine($"LONG Start {name} Thread={Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(2500);
Console.WriteLine($"LONG End {name} Thread={Thread.CurrentThread.ManagedThreadId}");
}
Outputs:
Start
SHORT Start SW1 Thread=1
SHORT Start SW2 Thread=1
LONG Start LW1 Thread=5
After starting LongWork Thread=5
SHORT End SW1 Thread=7
SHORT End SW2 Thread=5
LONG End LW1 Thread=5
Done
Long work starts on thread 5, but at some point thread 5 is released back to the threadpool as thread 5 is able to pick up Short SW1 ending. When exactly does 5 get released back to the threadpool after await Task.Delay(2500) in LongWork? Does the await call release it back to the threadpool? I dont think this is the case as if I log the thread id right after the call to LongWork, that is still running on thread 5. await Task.WhenAll is called on thread 5 - which then releases control back up to whatever called 'main' - is this where it gets released as there is no 'caller' to go back to?
My understanding of what happens:
Starts on thread 1, thread 1 executes ShortWork SW1 and SW2.
Task.Delay(50) is awaited and thread 1 gets released (as there is no more work to do?)
Thread 5 is chosen to pick up the continuation after the 50ms delay
Thread 5 kicks off LongWork, and it it gets to the awaited 2500ms delay. Control gets released back up to main, still on thread 5. t1 and t2 are awaited - control gets released back up to whatever called main (and so thread 5's work is done - it gets released to the threadpool)
At this point no threads are 'doing' anything
When the ShortWork delay is done, thread 5 and 7 are selected from the pool for the continuation of each call. Once done with the continuation, these are released to the pool (?)
Another thread picks up the continuation between Task.WhenAll and await t3, which then immediately gets released because it is just awaiting t3
A ThreadPool thread is selected to do the continuation of the LongWork call
Finally, a ThreadPool thread picks up the last work to write done after t3 is done.
Also as a bonus, why does 5 pick up end of SW1, and 7 pick up end of LW1? These are the threads that were just used. Are they somehow kept as 'hot threads and prioritised for continuations that come up?
The way await works is that it first checks its awaitable argument; if it has already completed, then the async method continues synchronously. If it is not complete, then the method returns to its caller.
A second key to understanding is that all async methods begin executing synchronously, on a normal call stack just like any other method.
The third useful piece of information here is that a Console app needs a foreground thread to keep running or it will exit. So when you have an async Main, behind the scenes the runtime blocks the main thread on the returned task. So, in your example, when the first await is hit in Main, it returns a task, and the main thread 1 spends the rest of the time blocked on that task.
In this code, all continuations are run by thread pool threads. It's not specified or guaranteed which thread(s) will run which continuations.
The current implementation uses synchronous continuations, so in your example the thread id for LONG Start LW1 and After starting LongWork will always be the same. You can even place a breakpoint on After starting LongWork and see how a LongWork continuation is actually in the call stack of your Main continuation.
What actually happens is that when a pool thread starts, it takes a task from the pool's work queue, waiting if necessary. It then executes the task, and then takes a new one, again waiting if necessary. This is repeated until the thread determines that it has to die and exits.
While the thread is taking a new task, or waiting for one, you could say that it has been "released to the thread pool", but that's not really useful or helpful. Nothing from the pool actually does things to the threads, after starting them up. The threads control themselves.
When you write an async function, the compiler transforms it in a way that divides it into many tasks, each of which will be executed by a function call. Where you write Task.Delay(), what actually happens is that your function schedules the task that represents the remainder of its execution, and then returns all the way out to the thread's main procedure, allowing it to get a new task from the thread pool's work queue.
Related
I have multi thread program with C# language.
When load main form, 4 thread start that all of them work with while(true)
Every thread run, active and exist in thread list but after 30 ms, one of thread (without any error) disappear from thread list and doesn't work
even breakpoint not work.
I want read data from TCP and process 3 steps then save to database
Task1: have while(true) and read data from tcp and add to blockingcollection bk1
Task 2: in while(true), take data from bk1 and process data then add data to BlockingCollection bk2
Task3 in while(true) take data from bk2 and process then bk3
Task 4 in while (true) take data from bk3 then insert database
I define task:
Task.Factory.StartNew(() => myfunction, CancellationToken.None,
TaskCreationOptions.PreferFairness, TaskScheduler.Default);
When click button1 all of tasks start and work correctly after some time task3 change status to RanToCompletion and does not work I didn't use async and await in code because task works parallel and don't need wait for other task.
Even TaskCreationOptions set to LongRunning
My function have while(true) and work producer-consumer method.
Please help me about problem.
Thanks for attention.
Your code never awaits that task to end. Use await and Task.Run instead:
await Task.Run(()=>myFunction());
or
await Task.Run(myFunction);
Tasks aren't threads. They're a job that gets executed by a threadpoool thread. Creating a thread is an expensive operation. To avoid the cost of creating and destroying threads, .NET keeps a pool of worker threads. Jobs, in the form of Action or Func delegates, get posted to that ThreadPool and executed by one of the worker threads.
Task.Run or Task.Factor.StartNew post a job to the threadpool for execution and return a Task, essentially a "promise" object. This means the calling thread isn't blocked waiting for the task to complete and can do other work. await makes it easy to await for that task to complete and get back to the calling thread. Once the task completes, execution can resume with the code after await.
In a desktop application that means the UI thread doesn't get blocked waiting for a task to complete and can keep processing Windows messages, button clicks, refresh its windows etc. When the task completes, execution will resume on the UI thread with the code that comes after await.
async void btn1_Click(object sender,EventArgs args)
{
var customerName=txtCustomer.Text;
var orders=await Task.Run(()=>LoadOrdersFromDbAsync(customerName));
grdOrders.DataSource=orders;
}
I'm reading a book on threads, below is the quote from the book:
When a thread calls the Wait method, the system checks if the Task that the
thread is waiting for has started executing. If it has, then the thread calling Wait will block
until the Task has completed running. But if the Task has not started executing yet, then
the system may (depending on the TaskScheduler) execute the Task by using the thread
that called Wait. If this happens, then the thread calling Wait does not block; it executes
the Task and returns immediately
Let's say we have the following code:
static void Main(string[] args) {
// Create a Task (it does not start running now)
Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10);
t.Start();
t.Wait();
Console.WriteLine("The Sum is: " + t.Result);
}
static Int32 Sum(Int32 n) {
Int32 sum = 0;
for (; n > 0; n--)
{ sum += n; }
return sum;
}
My understanding is, t.Start(); means CLR will schedule it for execution so later a worker thread from threadpool can execute it. So let's say the main thread has reached to t.Wait(); while the task is still not executed by any worker thread (task has been scheduled but hasn't been picked up by the thread pool's worker thread).
Then main thread will actually execute the Sum method, is my understanding correct? So if I run the program a thousand of times, 99.99% Sum method will be executed by other worker thread, but 0.01% it will be executed by main thread. Currently no matter how many time I run the program it is always a woker thread to execute Sum method, this is just because the task always start to execute quicke before the main thread executes t.Wait();, if sth usunal happen, there is still a chance (0.01%) that the main thread can execute Sum method, is my understanding correct?
if [the unusual happens], there is still a chance (0.01%) that the main thread can execute Sum method, is my understanding correct?
The answer is possibly!
This involves some serious deep-diving into the specific implementation details of how the TaskScheduler handles tasks that are queued and how the compiler and subsequent smoke and mirrors that run in the back-end of the MSDN language have implemented the state-machines that handle Tasks and async operations.
But in the basic sense you're conclusion, in my opinion, is correct! Per MSDN, if you are using the default TaskScheduler, the TaskScheduler implements certain optimizations on your behalf such as Task Inlining, like you described, and other optimizations like Work Stealing.
If work stealing and task inlining are not desirable, consider specifying a synchronization context of the tasks that you are creating, to prevent them from performing work on threads other than the ones you specify.
I has spend a lot of time to understand async programming principles. But one thing is still unclear. I was confused by this code :
static async Task Method()
{
Console.WriteLine($"Method entered.");
await Task.Delay(1000);
Console.WriteLine($"Await 1 finished.");
await Task.Delay(1000);
Console.WriteLine($"Await 2 finished");
}
static int Main(string[] args)
{
Console.WriteLine($"Main started.");
return AsyncContext.Run(() => MainAsync(args));
}
static async Task<int> MainAsync(string[] args)
{
var t = Method();
Console.WriteLine("Thread starting sleep.");
Thread.Sleep(10000);
Console.WriteLine("Thread stopped sleeping");
Console.WriteLine(t.IsCompleted ? "Method completed" : "Method not completed");
await t;
return 0;
}
Result :
Main started.
Method entered.
Thread starting sleep.
Thread stopped sleeping
Method not completed
Await 1 finished.
Await 2 finished
As I understand while Main thread is sleeping IO-bound operations from Method should be executed (cause Task.Delay emulate IO) and interrupt Main thread sequentially to continue execute Method code.
So I expect to see:
Main started.
Method entered.
Thread starting sleep.
Await 1 finished.
Await 2 finished
Thread stopped sleeping
Method completed
I know that by Thread.Sleep I am making to stop Main thread. But as I understand Method() should not need thread because it consists of IO-bound operations.
Can anybody explain where I am misunderstanding it?
AsynContext that I am using is (here).
By default "await" captures the current synchronization context and spawns the continuation in that original context.
If the context is undefined, continuation is spawned in the default thread pool (TaskScheduler.Default).
I'm not familiar with AsyncContext, but it probably spawns MainAsync under some well synchronization context and since Thread.Sleep blocks the thread that occupies that context, the continuation of "await" will wait until the context is freed.
This is not a strange phenomenon, you can reproduce it without the AsyncContext class. Try to run the same code in a Windows forms application and you will see.
Windows forms have it's own synchronization context that guards against unsynchronized Control manipulation.
To overcome this, you could tell "await" to not capture the synchronization context by using the ConfigureAwait(false) method.
static async Task Method()
{
Console.WriteLine($"Method entered.");
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine($"Await 1 finished.");
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine($"Await 2 finished");
}
await won't try to spawn continuation in the existing context, rather it would spawn it in a thread pool task.
Why your code is behaving correctly as expected ?
On using the AsyncContext.Run, you are providing an explicit context for Console application which otherwise have NULL Synchronization Context, now when you execute the following lines of code in MainAsync:
var t = Method();
Console.WriteLine("Thread starting sleep.");
Thread.Sleep(10000);
Then Method() starts executing, where on encountering the statement:
await Task.Delay(1000);
It cedes the control back to the caller, where you block the context by making the Thread Sleep for 10s Thread.Sleep(10000);, so now before this sleep is over the Continuation cannot take place in the Async method, since it waits for the Continuation context to be available, moment its free, then it starts executing the Continuation, but by that time it also complete the remaining statements in the MainAsync, which seems to be prioritized and response is as expected, it awaits only in the very end, in fact checking the Task status for any logic like t.IsCompleted is more of a code smell, better is only await t, which waits for the Task completion
There are two ways to get the behavior that you expect
As shown by #Arik, configure both await using ConfigureAwait(false), what does this means, simple that for running the Async continuation it doesn't need the original context and that will continue as true Async operation, hence will provide the result as you expect. Most of the libraries wit Async functions especially IO based, implement ConfigureAwait(false).
Make the call from the Main as return MainAsync(args).Result;, this will ensure standard behavior of the Console applications, which means NULL Synchronization Context, which means Async doesn't care about continuation on any existing Context, it goes on in background even when you are making the Thread sleep, since it doesn't expect that context and the result would be same as you expect
Main started.
Method entered.
Thread starting sleep.
Await 1 finished.
Await 2 finished
Thread stopped sleeping
Method completed
0
AsyncContext schedules all tasks to be executed on a single thread. Your Method consists of Delays and WriteLines. You may think of Delays as analogous to IO operations in that they do not need a thread to be executed on. However, WriteLine requires a thread. Thus, when Method is awaken from Delay it waits for thread be available to execute WriteLine.
Actually the Method would block even if it does not contain WriteLines but only Delays because it needs a thread for the Delay to return to and to start new Delay, but that would be more difficult to notice without WriteLines.
I'm trying to understand async/await and have read a number of articles but am still confused about the synchronous/asynchronous nature.
I have the following test console app:
static void Main(string[] args)
{
var test = FooAsync();
Console.WriteLine("After FooAsync");
for (int i = 0; i < 100; i++)
Console.WriteLine("After that");
Console.ReadKey();
}
private static async Task FooAsync()
{
Console.WriteLine("Before delay");
await Task.Delay(1);
Console.WriteLine("After delay");
}
The code gives output along the lines of:
Before delay
After FooAsync
After that
After that
After that
After that
After delay
After that
.
.
I understand that async/await will not create a separate thread for processing and that at the point FooAsync reaches the await Task.Delay(1) line it will return back to Main as the task will not yet have completed, however, as we are only running on a single thread can someone explain what triggers the FooAsync method to resume at some arbitrary point within Main before Main can then continue?
Update
I take it back and i3arnon and dariogriffo are correct. The code does use multiple threads (as I'd have seen before had looked in the debugger or done the obvious as kha suggested). I'd been confused by the Threads section on the following page https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_Threads not realising that a "continuation" actually refers to a continuation task schedule to run as soon as the task being "awaited" finishes.
This isn't single threaded.
When the delay task completes the rest of the method is posted to the ThreadPool and runs concurrently with your main thread. The "trigger" here is the callback of the internal System.Threading.Timer being used inside Task.Delay.
This behaviour depends on the SynchronizationContext. In a UI environment this would have been posted to the same UI thread and would have to wait until that thread is free.
If you would have been waiting for the task returned from FooAsync then you would only have a single thread running each time.
Async/await may create new threads OR NOT, it depends of the nature of the operation.
If the operation is an IO (for example disks/network operations) probably is coded in a way it will not spin a new thread. You can read from here:
The async and await keywords don't cause additional threads to be created?
If you create your own Async operation and you create a thread, that's a different story, that's why you shouldn't do async over sync
http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx
You can check this also but using Thread.CurrentThread to get the Id of the process. (Add that to a Console.WriteLine)
It's a pretty common misconception that the async or await keywords create new threads. They don't.
The threads are created by running a Task. In this case, the thread is created by the Task.Delay call.
I find that when I call an "async" function, at first, the thread is the main thread. When it meets the await Task, then the thread will be changed to another one.
static void Main(string[] args)
{
println("Mainthread ID " + Thread.CurrentThread.GetHashCode());
var s = DoSomethingAsync();
println("Waiting for DoSomethingAsync Complete ");
Thread.Sleep(2000);
println(s.Result.ToString());
}
static async Task<int> DoSomethingAsync()
{
println("DoSomethingAsync Thread ID " + Thread.CurrentThread.GetHashCode());
int val = await Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
println("Task Thread ID " + Thread.CurrentThread.GetHashCode());
return 100;
});
println("DoSomethingAsync end " + Thread.CurrentThread.GetHashCode());
return val;
}
And the result is as follow:
11:40:34 383 Mainthread ID 1
11:40:34 398 DoSomethingAsync Thread ID 1
11:40:34 400 Waiting for DoSomethingAsync Complete
11:40:35 400 Task Thread ID 3
11:40:35 400 DoSomethingAsync end 3
11:40:36 401 100
We can see the mainthread id is 1. DoSomethingAsync Thread ID is also 1. They are same.
So at the beginning of running "async" function, it's the main thread that do this part.
This means when the main thread meet the "async" function, it will be blocked to run something until it meet the await Task.The "async" function doesn't make things asynchronous instantly. Is it right?
I know that if there's no await Task in the "async" function ,this "async" function will be run synchronously.
This means when the main thread meet the "async" function, it will be
blocked to run something until it meet the await Task.The "async"
function doesn't make things asynchronous instantly. Is it right?
Yes, that is right. Your async method will run until hitting its first await keyword, which will yield control back to the caller after that. In your example, you don't await on that method, so it will continue to execute the next method right away.
As a side note, a Console Application doesn't really have a Main Thread, as it uses the default ThreadPoolSynchronizationContext. That means that you'll see any arbitrary thread id after your awaited async method.
The method will start running in whatever thread calls the method. If the method is called from the main thread, it'll start running in the main thread. If it's called from a thread pool thread it'll start running in a thread pool thread. It is no different from any other method in that regard.
Continuations after any await operation will leverage the current SyncrhonizationContext to determine how they are scheduled.