Having created the following console application I am a little puzzled why it seems to run synchronously instead of asynchronously:
class Program
{
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var total = CreateMultipleTasks();
stopwatch.Stop();
Console.WriteLine("Total jobs done: {0} ms", total.Result);
Console.WriteLine("Jobs done in: {0} ms", stopwatch.ElapsedMilliseconds);
}
static async Task<int> CreateMultipleTasks()
{
var task1 = WaitForMeAsync(5000);
var task2 = WaitForMeAsync(3000);
var task3 = WaitForMeAsync(4000);
var val1 = await task1;
var val2 = await task2;
var val3 = await task3;
return val1 + val2 + val3;
}
static Task<int> WaitForMeAsync(int ms)
{
Thread.Sleep(ms);
return Task.FromResult(ms);
}
}
When running the application, output is:
Total jobs done: 12000 ms
Jobs done in: 12003 ms
I would have expected somehing like:
Total jobs done: 12000 ms
Jobs done in: 5003 ms
Is this because when I use the Thread.Sleep method it stops further execution of the whole application? Or am I missing something important here?
Even when you convert to using Task.Run or Task.Delay as other answers suggest, you should avoid using the blocking Task.WaitAll anywhere inside async methods, as much as you can. Mixing asynchronous and synchronous code is usually a bad idea, it increases the number of redundantly blocked threads and promotes deadlocks.
Instead, use await Task.WhenAll and move the blocking wait to the top level (i.e., Main method in this case):
class Program
{
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var total = CreateMultipleTasks();
total.Wait();
stopwatch.Stop();
Console.WriteLine("Total jobs done: {0} ms", total.Result);
Console.WriteLine("Jobs done in: {0} ms", stopwatch.ElapsedMilliseconds);
}
static async Task<int> CreateMultipleTasks()
{
var task1 = Task.Run(() => WaitForMeAsync(5000));
var task2 = Task.Run(() => WaitForMeAsync(3000));
var task3 = Task.Run(() => WaitForMeAsync(4000));
await Task.WhenAll(new Task[] { task1, task2, task3 });
return task1.Result + task2.Result + task3.Result;
}
static int WaitForMeAsync(int ms)
{
// assume Thread.Sleep is a placeholder for a CPU-bound work item
Thread.Sleep(ms);
return ms;
}
}
On a side note, check Stephen Toub's "Should I expose asynchronous wrappers for synchronous methods?" and "Should I expose asynchronous wrappers for synchronous methods?"
You run the task in a synchounus manner. You can do something like this:
static async Task<int> CreateMultipleTasks()
{
var task1 = Task.Run<int>(() => WaitForMeAsync(5000));
var task2 = Task.Run<int>(() => WaitForMeAsync(3000));
var task3 = Task.Run<int>(() => WaitForMeAsync(4000));
Task.WaitAll(new Task[] { task1, task2, task3 });
return task1.Result + task2.Result + taks3.Result;
}
Using the three await in a row will NOT run the tasks in parallel. It will just free the thread while it is waiting (if you use await Task.Delay(ms) as Thread.Sleep(ms) is a blocking operation), but the current execution will NOT continue with task2 while task1 is "sleeping".
Your WaitForMeAsync method is just a simple sync method pretending to be an async one.
You don't perform anything async and Sleep() just blocks the thread.
Why would you want to delay async? (yes you can use await Task.Delay(ms)), need more input to help there.
Related
This question already has answers here:
Why this task hangs?
(2 answers)
Execution of tasks hangs with Task.WaitAll in C#
(2 answers)
Closed 1 year ago.
I am failing to understand why calling a async method from the Main method from a console application hangs forever. I feel I am missing some key element of asynchronous execution:
static void Main(string[] args)
{
Console.WriteLine("w3");
var exe = new Exe2();
exe.Do2().Wait();
Console.WriteLine("/w3");
Console.ReadKey();
}
public class Exe2
{
public async Task Do2()
{
Task task1 = new Task(() => { Console.WriteLine("t1"); Task.Delay(2000); });
Task task2 = new Task(() => { Console.WriteLine("t2"); Task.Delay(2000); });
Task task3 = new Task(() => { Console.WriteLine("t3"); Task.Delay(2000); });
await Task.WhenAll(task1, task2, task3);
}
}
The code above prints w3 not nothing else, and doesn't take the ReadKey either.
You haven't started your tasks, Task.WhenAll is essentially going to wait forever. You would have needed to call Start
Starts the Task, scheduling it for execution to the current
TaskScheduler.
task1.Start();
task2.Start();
task3.Start();
await Task.WhenAll(task1, task2, task3);
However : NEVER use the Task constructor unless you absolutely know you need it, it has several very big traps for unseasoned coders such as only supporting the action delegate and needing to be started. Instead ALWAYS use Task.Run which will start your tasks hot
Task task1 = Task.Run(() => { Console.WriteLine("t1"); Task.Delay(2000); });
Task task2 = Task.Run(() => { Console.WriteLine("t2"); Task.Delay(2000); });
Task task3 = Task.Run(() => { Console.WriteLine("t3"); Task.Delay(2000); });
Although not your problem, you should NEVER need to call Wait or Result in modern .net and even more so on an async method. Instead, use the async Main overload
static async Task Main(string[] args)
{
Console.WriteLine("w3");
var exe = new Exe2();
await exe.Do2();
Console.WriteLine("/w3");
Console.ReadKey();
}
I am thinking a way to handle and expensive process which would require me to call and get multiple data from multiple Industrial field. So for each career I will make them into one individual task inside the Generate Report Class. I would like to ask that when using Task.WhenAll is it that task 1,task 2,task 3 will run together without waiting task1 to be completed. But if using Task.WaitAll it will wait for MiningSector to Complete in order to run ChemicalSector. Am I correct since this is what I wanted to achieve.
public static async Task<bool> GenerateReport()
{
static async Task<string> WriteText(string name)
{
var start = DateTime.Now;
Console.WriteLine("Enter {0}, {1}", name, start);
return name;
}
static async Task MiningSector()
{
var task1 = WriteText("task1");
var task2 = WriteText("task2");
var task3 = WriteText("task3");
await Task.WhenAll(task1, task2, task3); //Run All 3 task together
Console.WriteLine("MiningSectorresults: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static async Task ChemicalsSector()
{
var task4 = WriteText("task4");
var task5 = WriteText("task5");
var task6 = WriteText("task6");
await Task.WhenAll(task4 , task5 , task6 ); //Run All 3 task together
Console.WriteLine("ChemicalsSectorresults: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static void Main(string[] args)
{
Task.WaitAll(MiningSector(), ChemicalsSector()); //Wait when MiningSectoris complete then start ChemicalsSector.
}
return true;
}
The thing I wanted to achieve is, inside MiningSector have 10 or more functions need to be run and ChemicalSector is same as well. I wanted to run all the functions inside the MiningSector at parallel once it's done then the ChemicalSector will start running the function.
But if using Task.WaitAll it will wait for MiningSector to Complete in order to run ChemicalSector. Am I correct since this is what I wanted to achieve.
No. The key thing to understand here is that arguments are evaluated first, and then the method call. This is the same way all arguments and method calls work across the entire C# language.
So this code:
Task.WaitAll(MiningSector(), ChemicalsSector());
has the same semantics as this code:
var miningTask = MiningSector();
var sectorTask = ChemicalsSector();
Task.WaitAll(miningTask, sectorTask);
In other words, both methods are called (and both tasks started) before Task.WaitAll is called.
If you want to do one then the other, then don't call the second method until the first task has completed. This is most easily done using async Main, e.g.:
static async Task Main(string[] args)
{
await MiningSector();
await ChemicalsSector();
}
Let's say I have a list of Tasks, and I want to run them in parallel. But I don't need all of them to finish to continue, I can move on with just one. The following code waits for all the tasks to finish to move on. Is there a way for me to individually continue with the task that has completed while waiting for the other ones to finish?
List<string>[] x = await Task.WhenAll(new Task<List<string>>[] { task1, task2 })
// When task1 finishes, I want to process the result immediately
// instead of waiting on task2.
You're probably looking for Task.WhenAny.
I've used it for setting off a pile of tasks and then processing each of them as they become ready, but I suppose you could also just wait for one to finish and continue without the loop if you don't care about dealing with the rest.
while(tasks.Count() > 0)
{
var task = await Task.WhenAny(tasks);
tasks.Remove(task);
var taskresult = await task;
// process result
}
If you are using C# 8 and .NET Core you can take advantage of IAsyncEnumerable to hide this complexity from the consuming side.
Just like this:
static async Task Main(string[] args)
{
await foreach (var data in GetData())
{
Console.WriteLine(data);
}
Console.ReadLine();
}
static async IAsyncEnumerable<string> GetData()
{
List<Task<string>> tasks = new List<Task<string>> {GetData1(), GetData3(), GetData2()};
while (tasks.Any())
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
yield return await finishedTask;
}
}
static async Task<string> GetData1()
{
await Task.Delay(5000);
return "Data1";
}
static async Task<string> GetData2()
{
await Task.Delay(3000);
return "Data2";
}
static async Task<string> GetData3()
{
await Task.Delay(2000);
return "Data3";
}
You can use Task.WhenAny instead.
Example "stolen" from Stephen Cleary's Blog:
var client = new HttpClient();
string results = await await Task.WhenAny(
client.GetStringAsync("http://example.com"),
client.GetStringAsync("http://microsoft.com"));
// results contains the HTML for whichever website responded first.
Responding to comment
You absolutely can keep track of the other tasks:
// supposing you have a list of Tasks in `myTasks`:
while( myTasks.Count > 0 )
{
var finishedTask = await Task.WhenAny(myTasks);
myTasks.Remove(finishedTask);
handleFinishedTask(finishedTask); // assuming this is a method that
// does the work on finished tasks.
}
The only thing you'd have to watch out for is :
The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
Remarks in WhenAny Doks(Emphasis by me)
In case you want to process the results in order of the completion of the tasks, there is the OrderByCompletion extension method that does exactly that in Stephen Cleary's Nito.AsyncEx library, with the signature below:
// Creates a new collection of tasks that complete in order.
public static List<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> #this);
Usage example:
Task<string>[] tasks = new[] { task1, task2, task3, task4 };
foreach (var task in tasks.OrderByCompletion())
{
string result = await task;
// Do something with result
}
If you prefer not having external dependencies, the source code is here.
Based on the answer of Peter Csala, here a extension method for IAsyncEnumerable:
public static async IAsyncEnumerable<T> OrderedByCompletion<T>(this IEnumerable<Task<T>> tasks)
{
List<Task<T>> taskList = new List<Task<T>>(tasks);
while (taskList.Count > 0)
{
var finishedTask = await Task.WhenAny(taskList);
taskList.Remove(finishedTask);
yield return await finishedTask;
}
}
I am working on a class DelayedExecutor that will delay the execution of an Action passed to its DelayExecute method by a certain time timeout (see code below) using the async and await statements. I also want to be able to abort the execution within the timeout interval if needed. I have written a small test to test its behavior like this:
Code for Test method:
[TestMethod]
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran function!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds 2: " + sw.ElapsedMilliseconds);
Thread.Sleep(1000);
}
}
The output I expected from this test was that it would show me:
sw.ElapsedMilliseconds outside DelayExecute 1: ...
Ran Action!"
sw.ElapsedMilliseconds inside DelayExecute: ...
sw.ElapsedMilliseconds outside DelayExecute 2:
However I get this and do not understand why:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
sw.ElapsedMilliseconds outside DelayExecute 2: 30
Ran Action!
sw.ElapsedMilliseconds inside DelayExecute: 1015
On this blog post I read:
I like to think of “await” as an “asynchronous wait”. That is to say, the async method pauses until the awaitable is complete (so it waits), but the actual thread is not blocked (so it’s asynchronous).
This seems to be inline with my expectation, so what is going on here and where is my error?
Code for DelayedExecutor:
public class DelayedExecutor
{
private int timeout;
private Task currentTask;
private CancellationToken cancellationToken;
private CancellationTokenSource tokenSource;
public DelayedExecutor(int timeout)
{
this.timeout = timeout;
tokenSource = new CancellationTokenSource();
}
public void AbortCurrentTask()
{
if (currentTask != null)
{
if (!currentTask.IsCompleted)
{
tokenSource.Cancel();
}
}
}
public Task DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
}
Update:
As suggested I modified this line inside my DelayExecute
return currentTask = Task.Factory.StartNew(async () =>
into
return currentTask = await Task.Factory.StartNew(async () =>
For this to work I needed to change the signature into this
public async Task<Task> DelayExecute(Action func)
so that my new definition is this:
public async Task<Task> DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = await Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
However now I have the same behavior as before. Is there some way achieve what I am trying to do using my design. Or is it fundamentally flawed?
By the way, I also tried putting await await delayTask; inside my test DelayExecuteTest, but this gives me the error
Cannot await 'void'
This is the updated test-method DelayExecuteTest, which does not compile:
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
Thread.Sleep(1000);
}
There's a bit going on here so I'm going to post a simple answer to begin with, let's see if it suffices.
You'we wrapped a task inside a task, this needs a double await. Otherwise you're only waiting for the inner task to reach its first return point, which will (can) be at the first await.
Let's look at your DelayExecute method in more detail. Let me begin with changing the return type of the method and we'll see how this changes our perspective. The change of return type is just a clarification. You're returning a Task that is nongeneric right now, in reality you're returning a Task<Task>.
public Task<Task> DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
(note that this will not compile since currentTask is also of type Task, but this is largely irrelevant if you read the rest of the question)
OK, so what happens here?
Let's explain a simpler example first, I tested this in LINQPad:
async Task Main()
{
Console.WriteLine("before await startnew");
await Task.Factory.StartNew(async () =>
{
Console.WriteLine("before await delay");
await Task.Delay(500);
Console.WriteLine("after await delay");
});
Console.WriteLine("after await startnew");
}
When executing this I get this output:
before await startnew
before await delay
after await startnew
after await delay -- this comes roughly half a second after previous line
So why this?
Well, your StartNew returns a Task<Task>. This is not a task that now will wait for the inner task to complete, it waits for the inner task to return, which it will (can) do at its first await.
So let's see the full execution path of this by numbering the interesting lines and then explaining the order in which things happen:
async Task Main()
{
Console.WriteLine("before await startnew"); 1
await Task.Factory.StartNew(async () => 2
{
Console.WriteLine("before await delay"); 3
await Task.Delay(500); 4
Console.WriteLine("after await delay"); 5
});
Console.WriteLine("after await startnew"); 6
}
1. The first Console.WriteLine executes
nothing magical here
2. We spin up a new task with Task.Factory.StartNew and await it.
Here our main method can now return, it will return a task that will continue once the inner task has completed.
The job of the inner task is not to execute all the code in that delegate. The job of the inner task is to produce yet another task.
This is important!
3. The inner task now starts executing
It will essentially execute all the code in the delegate up to and including the call to Task.Delay, which returns a Task.
4. The inner task arrives at its first await
Since this will not already have completed (it will only complete roughly 500ms later), this delegate now returns.
Basically, the await <this task we got from Task.Delay> statement will create a continuation and then return.
This is important!
6. Our outer task now continues
Since the inner task returned, the outer task can now continue. The result of calling await Task.Factory.StartNew is yet another task but this task is just left to fend for itself.
5. The inner task, continues roughly 500ms later
This is now after the outer task has already continued, potentially finished executing.
So in conclusion your code executes "as expected", just not the way you expected.
There are several ways to fix this code, I'm simply going to rewrite your DelayExecute method to the simplest way to do what you want to do:
Change this line:
return currentTask = Task.Factory.StartNew(async () =>
Into this:
return currentTask = await Task.Factory.StartNew(async () =>
This will let the inner task start your stopwatch and reach the first await before returning all the way out of DelayExecute.
The similar fix to my LINQPad example above would be to simply change this line:
await Task.Factory.StartNew(async () =>
To this:
await await Task.Factory.StartNew(async () =>
After giving it some more thought I figured out, how to achieve what I was aiming for, which is to delay the start of an action and be able to abort it in case I need to. Credit goes to Lasse V. Karlsen for helping me understand the issue.
The answer by Lasse is correct for my original problem, which is why I accepted it. But in case somebody needs to achieve something similar to what I needed, here is how I solved it. I had to use a continuation task after the task started with StartNew. I also renamed the method AbortCurrentTask into AbortExecution(). The new version of my DelayedExecutor class is this:
public class DelayedExecutor
{
private int timeout;
private Task currentTask;
private CancellationToken cancellationToken;
private CancellationTokenSource tokenSource;
public DelayedExecutor(int timeout)
{
this.timeout = timeout;
tokenSource = new CancellationTokenSource();
}
public void AbortExecution()
{
if (currentTask != null)
{
if (!currentTask.IsCompleted)
{
tokenSource.Cancel();
}
}
}
public Task DelayExecute(Action func)
{
AbortExecution();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask =
Task.Delay(timeout, cancellationToken).ContinueWith(t =>
{
if(!t.IsCanceled)
{
var sw = Stopwatch.StartNew();
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
}
});
}
}
This class now does what I expected from it. These two tests I now give the expected output:
[TestMethod]
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
}
Output:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
Ran Action!
sw.ElapsedMilliseconds inside DelayExecute: 3
sw.ElapsedMilliseconds outside DelayExecute 2: 1020
and
[TestMethod]
public async Task AbortDelayedExecutionTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
Thread.Sleep(100);
delayExecutor.AbortExecution();
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
}
Output:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
sw.ElapsedMilliseconds outside DelayExecute 2: 122
Why does running a hundred async tasks take longer than running a hundred threads?
I have the following test class:
public class AsyncTests
{
public void TestMethod1()
{
var tasks = new List<Task>();
for (var i = 0; i < 100; i++)
{
var task = new Task(Action);
tasks.Add(task);
task.Start();
}
Task.WaitAll(tasks.ToArray());
}
public void TestMethod2()
{
var threads = new List<Thread>();
for (var i = 0; i < 100; i++)
{
var thread = new Thread(Action);
threads.Add(thread);
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
}
private void Action()
{
var task1 = LongRunningOperationAsync();
var task2 = LongRunningOperationAsync();
var task3 = LongRunningOperationAsync();
var task4 = LongRunningOperationAsync();
var task5 = LongRunningOperationAsync();
Task[] tasks = {task1, task2, task3, task4, task5};
Task.WaitAll(tasks);
}
public async Task<int> LongRunningOperationAsync()
{
var sw = Stopwatch.StartNew();
await Task.Delay(500);
Debug.WriteLine("Completed at {0}, took {1}ms", DateTime.Now, sw.Elapsed.TotalMilliseconds);
return 1;
}
}
As far as can tell, TestMethod1 and TestMethod2 should do exactly the same. One uses TPL, two uses plain vanilla threads. One takes 1:30 minutes, two takes 0.54 seconds.
Why?
The Action method is currently blocking with the use of Task.WaitAll(tasks). When using Task by default the ThreadPool will be used to execute, this means you are blocking the shared ThreadPool threads.
Try the following and you will see equivalent performance:
Add a non-blocking implementation of Action, we will call it ActionAsync
private Task ActionAsync()
{
var task1 = LongRunningOperationAsync();
var task2 = LongRunningOperationAsync();
var task3 = LongRunningOperationAsync();
var task4 = LongRunningOperationAsync();
var task5 = LongRunningOperationAsync();
Task[] tasks = {task1, task2, task3, task4, task5};
return Task.WhenAll(tasks);
}
Modify TestMethod1 to properly handle the new Task returning ActionAsync method
public void TestMethod1()
{
var tasks = new List<Task>();
for (var i = 0; i < 100; i++)
{
tasks.Add(Task.Run(new Func<Task>(ActionAsync)));
}
Task.WaitAll(tasks.ToArray());
}
The reason you were having slow performance is because the ThreadPool will "slowly" spawn new threads if required, if you are blocking the few threads it has available, you will encounter a noticeable slowdown. This is why the ThreadPool is only intended for running short tasks.
If you are intending to run a long blocking operation using Task then be sure to use TaskCreationOptions.LongRunning when creating your Task instance (this will create a new underlying Thread rather than using the ThreadPool).
Some further evidence of the ThreadPool being the issue, the following also alleviates your issue (do NOT use this):
ThreadPool.SetMinThreads(500, 500);
This demonstrates that the "slow" spawning of new ThreadPool threads was causing your bottleneck.
Tasks are executed on threads from the threadpool. The threadpool as a limited number of threads which are reused. All task, or all requested actions, are queued and executed by those threads when they are idle.
Let's assume your threadpool has 10 threads, and you have 100 tasks waiting, then 10 tasks are executed while the other 90 tasks are simply waiting in the queue untill the first 10 tasks are finished.
In the second testmethod you create 100 threads who are dedicated to their tasks. So instead of 10 threads running simultaniously, 100 threads are doing the work.