My understanding of the await keyword was that the code following the await qualified statement is running as the continuation of that statement once it is complete.
Hence the following two versions should produce the same output:
public static Task Run(SemaphoreSlim sem)
{
TraceThreadCount();
return sem.WaitAsync().ContinueWith(t =>
{
TraceThreadCount();
sem.Release();
});
}
public static async Task RunAsync(SemaphoreSlim sem)
{
TraceThreadCount();
await sem.WaitAsync();
TraceThreadCount();
sem.Release();
}
But they do not!
Here is the complete program:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CDE
{
class Program
{
static void Main(string[] args)
{
try
{
var sem = new SemaphoreSlim(10);
var task = Run(sem);
Trace("About to wait for Run.");
task.Wait();
Trace("--------------------------------------------------");
task = RunAsync(sem);
Trace("About to wait for RunAsync.");
task.Wait();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
Trace("Press any key ...");
Console.ReadKey();
}
public static Task Run(SemaphoreSlim sem)
{
TraceThreadCount();
return sem.WaitAsync().ContinueWith(t =>
{
TraceThreadCount();
sem.Release();
});
}
public static async Task RunAsync(SemaphoreSlim sem)
{
TraceThreadCount();
await sem.WaitAsync();
TraceThreadCount();
sem.Release();
}
private static void Trace(string fmt, params object[] args)
{
var str = string.Format(fmt, args);
Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, str);
}
private static void TraceThreadCount()
{
int workerThreads;
int completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Trace("Available thread count: worker = {0}, completion port = {1}", workerThreads, completionPortThreads);
}
}
}
Here is the output:
[9] Available thread count: worker = 1023, completion port = 1000
[9] About to wait for Run.
[6] Available thread count: worker = 1021, completion port = 1000
[9] --------------------------------------------------
[9] Available thread count: worker = 1023, completion port = 1000
[9] Available thread count: worker = 1023, completion port = 1000
[9] About to wait for RunAsync.
[9] Press any key ...
What am I missing?
async-await optimizes for when the task you're awaiting on has already completed (which is the case when you have a semaphore set to 10 with only 1 thread using it). In that case the thread just carries on synchronously.
You can see that by adding an actual asynchronous operation to RunAsync and see how it changes the thread pool threads being used (which would be the behavior when your semaphore is empty and the caller actually needs to wait asynchronously):
public static async Task RunAsync(SemaphoreSlim sem)
{
TraceThreadCount();
await Task.Delay(1000);
await sem.WaitAsync();
TraceThreadCount();
sem.Release();
}
You can also make this change to Run and have it execute the continuation synchronously and get the same results as in your RunAsync (thread count wise):
public static Task Run(SemaphoreSlim sem)
{
TraceThreadCount();
return sem.WaitAsync().ContinueWith(t =>
{
TraceThreadCount();
sem.Release();
}, TaskContinuationOptions.ExecuteSynchronously);
}
Output:
[1] Available thread count: worker = 1023, completion port = 1000
[1] Available thread count: worker = 1023, completion port = 1000
[1] About to wait for Run.
[1] --------------------------------------------------
[1] Available thread count: worker = 1023, completion port = 1000
[1] Available thread count: worker = 1023, completion port = 1000
[1] About to wait for RunAsync.
[1] Press any key ...
Important Note: When it's said that async-await acts as a continuation it's more of an analogy. There are several critical difference between these concepts, especially regarding SynchronizationContexts. async-await automagically preserves the current context (unless you specify ConfigureAwait(false)) so you can use it safely in environments where that matters (UI, ASP.Net, etc.). More about synchronization contexts here.
Also, await Task.Delay(1000); may be replaced with await Task.Yield(); to illustrate that the timing is irrelevant and just the fact that the method waits asynchronously matters. Task.Yield() is often useful in unit tests of asynchronous code.
They won't as when you're calling async method, it's starting immediately. So, as long as your semaphore is not locked, WaitAsync() won't even start and there'll be no context switching (it's kind of optimization, same is applied to the canceled tasks), so your async method will be synchronous.
Meanwhile continuation version will actually start continuation on the parallel thread.
Related
Related to this question: Does await completely blocks the thread?
[...] it will first check to see if the called method completed, and if not will register the continuation and return from that method call. Later, once that method completes, it will re-enter the state-machine in order to complete the method
And to this question also: When is the best place to use Task.Result instead of awaiting Task
await simply means "this workflow cannot progress further until this task is completed, so if it is not complete, find more work to do and come back later"
And finally to this post: https://blog.stephencleary.com/2012/02/async-and-await.html
If “await” sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method. Later on, when the awaitable completes, it will execute the remainder of the async method. If you’re awaiting a built-in awaitable (such as a task), then the remainder of the async method will execute on a “context” that was captured before the “await” returned.
So from these posts I get that the await operator does indeed not block, but when I've tried to test it i just cannot get this principle to work the way it states to work. Obviously I'm missing something:
//This will take 10 seconds
[HttpGet("test1")]
public async Task<TimeSpan> test()
{
var t1 = DateTime.Now;
var wait1 = DoAsyncEcho("The first!", 10000);
var wait2 = DoAsyncEcho("The second!", 10000);
_logger.LogInformation(await wait1);
_logger.LogInformation(await wait2);
_logger.LogInformation("DONE!");
var t2 = DateTime.Now;
return t2 - t1;
}
//This will take 10 seconds too
[HttpGet("test2")]
public async Task<TimeSpan> test2()
{
var t1 = DateTime.Now;
var wait1 = DoAsyncEcho("The first!", 10000);
var wait2 = DoAsyncEcho("The second!", 10000);
Thread.Sleep(10000);
_logger.LogInformation(await wait1);
_logger.LogInformation(await wait2);
_logger.LogInformation("DONE!");
var t2 = DateTime.Now;
return t2 - t1;
}
//This will take 20
[HttpGet("test3")]
public async Task<TimeSpan> test3()
{
var t1 = DateTime.Now;
var wait1 = await DoAsyncEcho("The first!", 10000);
var wait2 = await DoAsyncEcho("The second!", 10000);
_logger.LogInformation(wait1);
_logger.LogInformation(wait2);
_logger.LogInformation("DONE!");
var t2 = DateTime.Now;
return t2 - t1;
}
//This will take 30
[HttpGet("test4")]
public async Task<TimeSpan> test4()
{
var t1 = DateTime.Now;
var wait1 = await DoAsyncEcho("The first!", 10000);
var wait2 = await DoAsyncEcho("The second!", 10000);
Thread.Sleep(10000);
_logger.LogInformation(wait1);
_logger.LogInformation(wait2);
_logger.LogInformation("DONE!");
var t2 = DateTime.Now;
return t2 - t1;
}
private Task<string> DoAsyncEcho(string v, int t)
{
return Task<string>.Factory.StartNew(() =>
{
Thread.Sleep(t);
return v;
}
);
}
As I see from the methods test3 and test4, await does indeed wait, it does not enter into a state-machine and does a callback later on because it waits the full 10 seconds of the first DoAsyncEcho and then another 10s on the second call. On the methods test1 and test2 execution time lasts for 10s as code does not waits for the return of the DoAsyncEcho but only it awaits for the result later. Particulary test2 method sleeps the 3 calls of 10 seconds in parallel so after all it's just a 10s run.
What do I'm missing here?
I think the best way to demonstrate this is through a simple Windows Forms app.
Create a default Windows Forms app and drop 3 buttons onto it (called button1, button2 and button3.
Then add the following code:
async void button1_Click(object sender, EventArgs e)
{
this.Text = "[button1_Click] About to await slowMethodAsync()";
int result = await slowMethodAsync();
this.Text = "[button1_Click] slowMethodAsync() returned " + result;
}
void button2_Click(object sender, EventArgs e)
{
this.Text = "[button2_Click] About to start task to call slowMethod()";
int result = 0;
Task.Run(() =>
{
result = slowMethod();
}).ContinueWith(_ =>
{
this.Invoke(new Action(() =>
{
this.Text = "[button2_Click] slowMethod() returned " + result;
}));
});
}
void button3_Click(object sender, EventArgs e)
{
this.Text = "[button3_Click] About to call slowMethod()";
int result = slowMethod();
this.Text = "[button3_Click] slowMethod() returned " + result;
}
static async Task<int> slowMethodAsync()
{
await Task.Delay(5000);
return 42;
}
static int slowMethod()
{
Thread.Sleep(5000);
return 42;
}
If you try this code out, you will notice the following:
Pressing button1 will immediately change the title to [button1_Click] About to await Task.Delay(5000), and you can resize the dialog while waiting for 5 seconds, after which the title will change to [button1_Click] Awaited Task.Delay(5000).
The code for handling button2 is very roughly equivalent to the state machine that is generated from the await code for button1. If you press button2, you will see similar effects to pressing button1.
(The actual code for await is in reality quite different, but the underlying mechanism of using a continuation - i.e., ContinueWith() and Invoke(), to continue executing the code after the await on the UI thread illustrates its approach.)
The code for button3 completely blocks during the Thread.Sleep(), and if you press button3 the UI locks up completely for 5 seconds,.
To illustrate what happens with a non-UI example, consider the following console application:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static async Task Main()
{
Console.WriteLine("Main thread ID = " + Thread.CurrentThread.ManagedThreadId);
int result = slowMethod();
Console.WriteLine("result = " + result);
Console.WriteLine("After calling slowMethod(), thread ID = " + Thread.CurrentThread.ManagedThreadId);
result = await slowMethodAsync();
Console.WriteLine("result = " + result);
Console.WriteLine("After calling slowMethodAsync(), thread ID = " + Thread.CurrentThread.ManagedThreadId);
}
static async Task<int> slowMethodAsync()
{
await Task.Delay(5000);
return 42;
}
static int slowMethod()
{
Thread.Sleep(5000);
return 42;
}
}
}
If you run that, you will see output similar to the following:
Main thread ID = 1
result = 42
After calling slowMethod(), thread ID = 1
result = 42
After calling slowMethodAsync(), thread ID = 4
Note how the code has resumed on a different thread after the await.
The key thing to realise is that as far as calling code is concerned, y = await X(); does not return until it has a value to return, and the code that runs afterwards may be running on a different thread.
The effect of this in terms of blocking THREADS is that the calling thread is freed up to go off and execute some other code, and another thread is only required when the async method returns.
In many cases, this means that no additional thread is required (for the continuation), and in all cases it means that the original calling thread is not blocked and can be freed up to the thread pool for use for another task.
This is the "non blocking" part of all this.
For a good, detailed explanation of why sometimes no additional thread is needed, read Stephen Cleary's excellent "There is no thread".
It seems that you are confusing two different interpretations of wait and block. The objective of asynchronous code is to block your code, while the thread remains unblocked. If you don't want to block your code, then the solution is easy: don't use await. But if you don't block your code, then you can't use the result of the asynchronous operation, because the asynchronous operation is running concurrently with your code.
What has not happened yet belongs to the future, and the future is unknown. Not only do you not know the result, you don't even know if the operation succeeded or failed. In most cases this is problematic. You need the result of the operation before continuing with processing this result. So you must block your code. And this is why await was invented, to block your code without having to block a thread too.
You need the thread to remain unblocked, so that it continues running the UI message pump that keeps your application responsive. Just because your code is blocked, your _application) need not to be blocked also. For ASP.NET applications, you need the thread to remain unblocked so that it can serve other incoming web requests. The fewer threads you block, the more requests you can serve. In this case async/await becomes a booster of scalability.
I may be missing something but what is the difference between doing:
public void MyMethod()
{
Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
t.Wait();
UpdateLabelToSayItsComplete();
}
public async void MyMethod()
{
var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
await result;
UpdateLabelToSayItsComplete();
}
private void DoSomethingThatTakesTime()
{
Thread.Sleep(10000);
}
I may be missing something
You are.
what is the difference between doing Task.Wait and await task?
You order your lunch from the waiter at the restaurant. A moment after giving your order, a friend walks in and sits down next to you and starts a conversation. Now you have two choices. You can ignore your friend until the task is complete -- you can wait until your soup arrives and do nothing else while you are waiting. Or you can respond to your friend, and when your friend stops talking, the waiter will bring you your soup.
Task.Wait blocks until the task is complete -- you ignore your friend until the task is complete. await keeps processing messages in the message queue, and when the task is complete, it enqueues a message that says "pick up where you left off after that await". You talk to your friend, and when there is a break in the conversation the soup arrives.
To demonstrate Eric's answer here is some code:
public void ButtonClick(object sender, EventArgs e)
{
Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
t.Wait();
//If you press Button2 now you won't see anything in the console
//until this task is complete and then the label will be updated!
UpdateLabelToSayItsComplete();
}
public async void ButtonClick(object sender, EventArgs e)
{
var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
await result;
//If you press Button2 now you will see stuff in the console and
//when the long method returns it will update the label!
UpdateLabelToSayItsComplete();
}
public void Button_2_Click(object sender, EventArgs e)
{
Console.WriteLine("Button 2 Clicked");
}
private void DoSomethingThatTakesTime()
{
Thread.Sleep(10000);
}
This example demonstrates the difference very clearly. With async/await the calling thread will not block and continue executing.
static void Main(string[] args)
{
WriteOutput("Program Begin");
// DoAsTask();
DoAsAsync();
WriteOutput("Program End");
Console.ReadLine();
}
static void DoAsTask()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
WriteOutput("2 - Task started");
t.Wait();
WriteOutput("3 - Task completed with result: " + t.Result);
}
static async Task DoAsAsync()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
WriteOutput("2 - Task started");
var result = await t;
WriteOutput("3 - Task completed with result: " + result);
}
static int DoSomethingThatTakesTime()
{
WriteOutput("A - Started something");
Thread.Sleep(1000);
WriteOutput("B - Completed something");
return 123;
}
static void WriteOutput(string message)
{
Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}
DoAsTask Output:
[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[3] B - Completed something
[1] 3 - Task completed with result: 123
[1] Program End
DoAsAsync Output:
[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[1] Program End
[3] B - Completed something
[3] 3 - Task completed with result: 123
Update: Improved example by showing the thread ID in the output.
Wait(), will cause to run potentially async code in sync manner. await will not.
For example, you have an asp.net web application. UserA calls /getUser/1 endpoint. asp.net app pool will pick a thread from thread pool (Thread1) and, this thread will make a http call. If you do Wait(), this thread will be blocked until http call resolves. While it is waiting, if UserB calls /getUser/2, then, app pool will need to serve another thread (Thread2) to make http call again. You just created (Well, fetched from app pool actually) another thread for no reason, because you cannot use Thread1 it was blocked by Wait().
If you use await on Thread1, then, SyncContext will manage sync between Thread1 and http call. Simply, it will notify once http call is done. Meanwhile, if UserB calls /getUser/2, then, you will use Thread1 again to make http call, because it was released once await got hit. Then another request can use it, even further more. Once http call is done (user1 or user2), Thread1 can get the result and return to caller (client). Thread1 was used for multiple tasks.
In this example, not much, practically. If you are awaiting a Task that returns on a different thread (like a WCF call) or relinquishes control to the operating system (like File IO), await will use fewer system resources by not blocking a thread.
In the example above, you can use "TaskCreationOptions.HideScheduler", and greatly modify the "DoAsTask" method. The method itself is not asynchronous, as it happens with "DoAsAsync" because it returns a "Task" value and is marked as "async", making several combinations, this is how it gives me exactly the same as using "async / await":
static Task DoAsTask()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic
TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task
WriteOutput("2 - Task started");
tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
return tsc.Task;
}
Can I await on a Task that was created on a different thread? For example:
...
CurrentIteration = Execute(); // returns Task
await CurrentIteration;
...
And then, on another thread:
...
await CurrentIteration;
...
Will the second thread wait for method Execute to finish executing?
If it will, will I be able to re-use CurrentIteration for the same purpose in the second thread, given that I re-run
CurrentIteration = Execute(); // returns Task
await CurrentIteration;
On the first thread?
I tried this code:
public class Program
{
public static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
public static async Task MainAsync(string[] args)
{
var instance = new SomeClass();
var task = instance.Execute();
Console.WriteLine("thread 1 waiting...");
Task.Run(async () =>
{
Console.WriteLine("thread 2 started... waiting...");
await task;
Console.WriteLine("thread 2 ended!!!!!");
});
await task;
Console.WriteLine("thread 1 done!!");
Console.ReadKey();
}
}
public class SomeClass
{
public async Task Execute()
{
await Task.Delay(4000);
}
}
But it prints
thread 1 waiting...
thread 2 started... waiting...
then
thread 1 done!!
but never thread 2 ended!!!!!. Why is that? How can I achieve that? Thanks!
You can await on a task from multiple threads. You were actually really close to get that to work, as #Rob said, you just needed to await the second thread.
consider this:
public static async Task MainAsync(string[] args)
{
var instance = new SomeClass();
var task = instance.Execute();
Console.WriteLine("thread 1 waiting...");
var secondTask = Task.Run(async () =>
{
Console.WriteLine("thread 2 started... waiting...");
await task;
Console.WriteLine("thread 2 ended!!!!!");
});
await task;
await secondTask;
Console.WriteLine("thread 1 done!!");
Console.ReadKey();
}
Add the wait on your second thread after you finish waiting for the task.
The reason you didn't see the indication is because the console got stuck on the ReadKey method, and couldn't write anything until it's finished. If you would've pressed Enter, you can see the "thread 2 ended!!!!!" line for a second before the app closes.
How can I check if a thread returned to the thread pool, using VS C# 2015 debugger?
What's problematic in my case is the fact that it cannot be detected by debugging line by line.
async Task foo()
{
int y = 0;
await Task.Delay(5);
// (1) thread 2000 returns to thread pool here...
while (y<5) y++;
}
async Task testAsync()
{
Task task = foo();
// (2) ... and here thread 2000 is back from the thread pool, to run the code below. I want
// to confirm that it was in the thread pool in the meantime, using debugger.
int i = 0;
while (i < 100)
{
Console.WriteLine("Async 1 before: " + i++);
}
await task;
}
In the first line of testAsync running on thread 2000, foo is called. Once it encounters await Task.Delay(5), thread 2000 returns to thread pool (allegedly, I'm trying to confirm this), and the method waits for Task.Delay(5) to complete. In the meantime, the control returns to the caller and the first loop of testAsync is executed on thread 2000 as well.
So between two consecutive lines of code, the thread returned to thread pool and came back from there. How can I confirm this with debugger? Possibly with Threads debugger window?
To clarify a bit more what I'm asking: foo is running on thread 2000. There are two possible scenarios:
When it hits await Task.Delay(5), thread 2000 returns to the thread pool for a very short time, and the control returns to the caller, at line (2), which will execute on thread 2000 taken from the thread pool. If this is true, you can't detect it easily, because Thread 2000 was in the thread pool during time between two consecutive lines of code.
When it hits await Task.Delay(5), thread 2000 doesn't return to thread pool, but immediately executes code in testAsync starting from line (2)
I'd like to verify which one is really happening.
There is a major mistake in your assumption:
When it hits await Task.Delay(5), thread 2000 returns to the thread pool
Since you don't await foo() yet, when thread 2000 hits Task.Delay(5) it just creates a new Task and returns to testAsync() (to int i = 0;). It moves on to the while block, and only then you await task. At this point, if task is not completed yet, and assuming the rest of the code is awaited, thread 2000 will return to the thread pool. Otherwise, if task is already completed, it will synchronously continue from foo() (at while (y<5) y++;).
EDIT:
what if the main method called testAsync?
When synchronous method calls and waits async method, it must block the thread if the async method returns uncompleted Task:
void Main()
{
var task = foo();
task.Wait(); //Will block the thread if foo() is not completed.
}
Note that in the above case the thread is not returning to the thread pool - it is completely suspended by the OS.
Maybe you can give an example of how to call testAsync so that thread 2000 returns to the thread pool?
Assuming thread 2k is the main thread, it cannot return to the thread pool. But you can use Task.Run(()=> foo()) to run foo() on the thread pool, and since the calling thread is the main thread, another thread pool thread will pick up that Task. So the following code:
static void Main(string[] args)
{
Console.WriteLine("main started on thread {0}", Thread.CurrentThread.ManagedThreadId);
var testAsyncTask = Task.Run(() => testAsync());
testAsyncTask.Wait();
}
static async Task testAsync()
{
Console.WriteLine("testAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
Console.WriteLine("testAsync continued on thread {0}", Thread.CurrentThread.ManagedThreadId);
}
Produced (on my PC) the following output:
main started on thread 1
testAsync started on thread 3
testAsync continued on thread 4
Press any key to continue . . .
Threads 3 and 4 came from and returned to the thread pool.
You can print out the Thread.CurrentThread.ManagedThreadId to the console. Note that the thread-pool is free to re-use that same thread to run continuations on it, so there's no guarantee that it'll be different:
void Main()
{
TestAsync().Wait();
}
public async Task FooAsync()
{
int y = 0;
await Task.Delay(5);
Console.WriteLine($"After awaiting in FooAsync:
{Thread.CurrentThread.ManagedThreadId }");
while (y < 5) y++;
}
public async Task TestAsync()
{
Console.WriteLine($"Before awaiting in TestAsync:
{Thread.CurrentThread.ManagedThreadId }");
Task task = foo();
int i = 0;
while (i < 100)
{
var x = i++;
}
await task;
Console.WriteLine($"After awaiting in TestAsync:
{Thread.CurrentThread.ManagedThreadId }");
}
Another thing you can check is ThreadPool.GetAvailableThreads to determine if another worker has been handed out for use:
async Task FooAsync()
{
int y = 0;
await Task.Delay(5);
Console.WriteLine("Thread-Pool threads after first await:");
int avaliableWorkers;
int avaliableIo;
ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);
Console.WriteLine($"Available Workers: { avaliableWorkers},
Available IO: { avaliableIo }");
while (y < 1000000000) y++;
}
async Task TestAsync()
{
int avaliableWorkers;
int avaliableIo;
ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);
Console.WriteLine("Thread-Pool threads before first await:");
Console.WriteLine($"Available Workers: { avaliableWorkers},
Available IO: { avaliableIo }");
Console.WriteLine("-------------------------------------------------------------");
Task task = FooAsync();
int i = 0;
while (i < 100)
{
var x = i++;
}
await task;
}
On my machine, this yields:
Thread-Pool threads before first await:
Available Workers: 1023, Available IO: 1000
----------------------------------------------
Thread-Pool threads after first await:
Available Workers: 1022, Available IO: 1000
I'd like to verify which one is really happening.
There is no way to "verify" that with debugger, because the debugger is made to simulate the logical (synchronous) flow - see Walkthrough: Using the Debugger with Async Methods.
In order to understand what is happening (FYI it's your case (2)), you need to learn how await works starting from Asynchronous Programming with Async and Await - What Happens in an Async Method section, Control Flow in Async Programs and many other sources.
Look at this snippet:
static void Main(string[] args)
{
Task.Run(() =>
{
// Initial thread pool thread
var t = testAsync();
t.Wait();
});
Console.ReadLine();
}
If we make the lambda to be async and use await t; instead of t.Wait();, this is the point where the initial thread will be returned to the thread pool. As I mentioned above, you cannot verify that with debugger. But look at the above code and think logically - we are blocking the initial thread, so if it' wasn't free, your testAsync and foo methods will not be able to resume. But they do, and this can easily be verified by putting breakpoint after await lines.
static void Main(string[] args)
{
// do async method for each stock in the list
Task<IEnumerable<string>> symbols = Helper.getStockSymbols("amex", 0);
List<Task> tasks = new List<Task>();
try
{
for (int i = 0; i < symbols.Result.Count(); i++)
{
if (i < symbols.Result.Count())
{
string symbol = symbols.Result.ElementAtOrDefault(i);
Task t = Task.Run(() => getCalculationsDataAsync(symbol, "amex"));
tasks.Add(t);
Task e = t.ContinueWith((d) => getThreadStatus(tasks));
}
}
// don't exit until they choose to
while (args.FirstOrDefault() != "exit")
{
// do nothing
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void getThreadStatus(List<Task> taskList)
{
int count = 0;
foreach (Task t in taskList)
{
if (t.Status == TaskStatus.Running)
{
count += 1;
}
}
Console.WriteLine(count + " threads are running.");
}
public static async Task getCalculationsDataAsync(string symbol, string market)
{
// do calculation here
}
What I'm trying to do in my code is run a new task for each stock in my list and run them all at the same time. I have a 4 core processor and I believe that means I can only run 4 tasks at once. I tried testing how many tasks were running by inserting the continuewith method that you see in my code that will tell me how many tasks are running. When I run this code, it tells me that 0 tasks are running so my questions are:
How can I complete my objective by running these tasks at the same exact time?
Why is it telling me that 0 tasks are running? I can only assume this is because the current task is finished and it hasn't started a new one yet if the tasks are running one after the other.
I am not sure why you are seeing no task running. Are you sure your Task.Run() is being hit? That is to say is i < symbols.Result.Count() satisfied?
Regardless of the above, let us try and achieve what you want. Firstly, no, it is not correct to say that because your machine has four physical cores/'threads', that you can use a maximum of four Threads. These are not the same things. A Google on this subject will bring a plethora of information your way about this.
Basically starting a thread the way you have will start background threads on a thread pool, and this thread pool can hold/govern numerous threads see Threading in C# by J. Albahari for more information. Whenever you start a thread, a few hundred microseconds are spent organizing such things as a fresh private local variable stack. Each thread also consumes (by default) around 1 MB of memory. The thread pool cuts these overheads by sharing and recycling threads, allowing multithreading to be applied at a very granular level without a performance penalty. This is useful when leveraging multicore processors to execute computationally intensive code in parallel in “divide-and-conquer” style.
The thread pool also keeps a lid on the total number of worker threads it will run simultaneously. Too many active threads throttle the operating system with administrative burden and render CPU caches ineffective. Once a limit is reached, jobs queue up and start only when another finishes.
Okay, now for your code. Let us say we have a list of stock types
List<string> types = new List<string>() { "AMEX", "AMEC", "BP" };
To dispatch multiple threads to do for for each of these (not using async/await), you could do something like
foreach (string t in types)
{
Task.Factory.StartNew(() => DoSomeCalculationForType(t));
}
This will fire of three background thread pool threads and is non-blocking, so this code will return to caller almost immediately.
If you want to set up post processing you can do this via continuations and continuation chaining. This is all described in the Albahari link I provided above.
I hope this helps.
--
Edit. to address comments:
Beginning with the .NET Framework version 4, the default size of the thread pool for a process depends on several factors, such as the size of the virtual address space. A process can call the GetMaxThreads method to determine the number of threads.
However, there's something else at play: the thread pool doesn't immediately create new threads in all situations. In order to cope with bursts of small tasks, it limits how quickly it creates new threads. IIRC, it will create one thread every 0.5 seconds if there are outstanding tasks, up to the maximum number of threads. I can't immediately see that figure documented though, so it may well change. I strongly suspect that's what you're seeing though. Try queuing a lot of items and then monitor the number of threads over time.
Basically let the thread pool dispatch what it wants, its optimizer will do the best job for your circumstance.
I think the problem is that the tasks have a status of Running either very briefly or can skip that status all together and go straight from WaitingForActivation to RanToCompletion.
I modified your program slightly and i can see the tasks starting and completing but they don't appear to be in the Running state whenever I check.
static void Main(string[] args)
{
// do async method for each stock in the list
Task<IEnumerable<string>> symbols = Task.FromResult(Enumerable.Range(1, 5).Select (e => e.ToString()));
List<Task> tasks = new List<Task>();
try
{
for (int i = 0; i < symbols.Result.Count(); i++)
{
string symbol = symbols.Result.ElementAtOrDefault(i);
Task t = getCalculationsDataAsync(symbol, "amex", tasks);
tasks.Add(t);
}
Console.WriteLine("Tasks Count:"+ tasks.Count());
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void getThreadStatus(List<Task> taskList)
{
int count = 0;
foreach (Task t in taskList)
{
Console.WriteLine("Status " + t.Status);
if (t.Status == TaskStatus.Running)
{
count += 1;
Console.WriteLine("A task is running");
}
}
//Console.WriteLine(count + " threads are running.");
}
public static async Task getCalculationsDataAsync(string symbol, string market, List<Task> tasks)
{
Console.WriteLine("Starting task");
var delay = new Random((int)DateTime.Now.Ticks).Next(5000);
Console.WriteLine("Delay:" + delay);
await Task.Delay(delay);
Console.WriteLine("Finished task");
getThreadStatus(tasks);
}
Output
Starting task
Delay:1784
Starting task
Delay:2906
Starting task
Delay:2906
Starting task
Delay:2906
Starting task
Delay:2906
Tasks Count:5
Finished task
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Finished task
Finished task
Finished task
Status RanToCompletion
Status RanToCompletion
Status WaitingForActivation
Status WaitingForActivation
Status RanToCompletion
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Status WaitingForActivation
Finished task
Status RanToCompletion
Status RanToCompletion
Status RanToCompletion
Status RanToCompletion
Status WaitingForActivation
As I describe on my blog, there are two kinds of tasks: Delegate Tasks and Promise Tasks. Only Delegate Tasks ever actually run. Promise Tasks only complete. The task returned by Task.Run is a Promise Task, not a Delegate Task; this is necessary so that Task.Run can understand async code and only complete when the async code is completed.
So, checking for TaskStatus.Running is not going to work the way you want. Instead, you can create a counter:
private static int _count;
static void Main(string[] args)
{
...
for (int i = 0; i < symbols.Result.Count(); i++)
{
if (i < symbols.Result.Count())
{
string symbol = symbols.Result.ElementAtOrDefault(i);
Task t = Task.Run(() => getCalculationsDataWithCountAsync(symbol, "amex"));
tasks.Add(t);
}
}
...
}
public static async Task getCalculationsDataWithCountAsync(string symbol, string market)
{
Console.WriteLine(Interlocked.Increment(ref _count) + " threads are running.");
try
{
await getCalculationsDataAsync(symbol, market);
}
finally
{
Console.WriteLine(Interlocked.Decrement(ref _count) + " threads are running.");
}
}
Note that I used a separate async "wrapper" method instead of messing with ContinueWith. Code using await instead of ContinueWith more correctly handles a number of edge cases, and IMO is clearer to read.
Also, remember that async and await free up threads while they're "awaiting." So, you can potentially have hundreds of (asynchronous) tasks going simultaneously; this does not imply that there are that many threads.