- •Table of Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •Version Support
- •Supported Versions
- •A Unified Platform
- •Roadmap
- •Supported Operating Systems
- •Command Line Interface
- •Desktop Development
- •Blazor
- •MAUI
- •Wrapping Up
- •.NET 6 Architecture
- •Runtimes
- •CoreCLR
- •Mono
- •WinRT
- •Managed Execution Process
- •Desktop Packs
- •Wrapping Up
- •Dotnet New
- •Dotnet Restore
- •NuGet.config
- •Dotnet Build
- •Dotnet Publish
- •Dotnet Run
- •Dotnet Test
- •Using the CLI in GitHub Actions
- •Other Commands
- •Wrapping Up
- •WinAPI
- •WinForms
- •STAThread
- •WinForms Startup
- •DPI Mode
- •Responding to Scale Events
- •Visual Styles
- •Text Rendering
- •The Message Loop
- •The Form Designer
- •WPF Startup
- •XAML Layout
- •Visual Tree
- •Data Binding
- •Windows App SDK
- •Building a Windows App SDK application
- •Using Windows APIs with Windows App SDK
- •Packaging
- •Migrating to .NET 6
- •Upgrade Assistant
- •Wrapping Up
- •Blazor WebAssembly
- •Creating a Blazor Wasm Project
- •Blazor Progressive Web Apps
- •Exploring the Blazor Client Project
- •Blazor in .NET 6
- •Blazor Component System
- •Creating Blazor Pages
- •Running a Blazor App
- •Blazor Server
- •SignalR
- •Blazor Desktop
- •Wrapping Up
- •Project Structure
- •Exploring MAUI
- •The Cross-Platform World
- •Application Lifecycle
- •MVVM
- •MVVM Toolkit
- •Wrapping Up
- •Model-View-Controller
- •Routing
- •Views
- •Controllers
- •Controller-Based APIs
- •Minimal APIs
- •Wrapping Up
- •Web Apps
- •Creating an App Service
- •Static Web Apps
- •Web App for Containers
- •Docker
- •Azure Functions
- •Deploying Azure Functions
- •Wrapping Up
- •Record Types
- •Monolith Architecture
- •Microservices
- •Container Orchestration
- •Kubernetes
- •Docker Compose
- •Dapr
- •Installing Dapr
- •Dapr State Management
- •Wrapping Up
- •Roslyn
- •Compiler API
- •Diagnostic API
- •Scripting API
- •Workspace API
- •Syntax Tree
- •Roslyn SDK
- •Source Generators
- •Writing a Source Generator
- •Debugging Source Generators
- •Wrapping Up
- •Garbage Collector
- •The Heap
- •The Stack
- •Garbage Collection
- •A Look at the Threadpool
- •Async in .NET 6
- •Await/Async
- •Cancellations
- •WaitAsync
- •Conclusion
- •Index
Chapter 11 Advanced .NET 6
A Look at the Threadpool
The threadpool in .NET is a pool of background threads that we can schedule work on. Depending on the system your application is running on, the runtime will create a set of background workers. Should we request more threadpool threads than the amount available, it will create extra background threads and keep those alive for future use. Since the threadpool threads are background threads, they cannot keep the
process alive. Once all foreground threads have exited, the application will close and all background threads will terminate.
The threadpool has been favored over creating threads manually ever since .NET 4. The main reason is performance; threadpool threads already exist; they just need to be given a unit of work, while manual threads still need to be created and that creation is an expensive operation. An easy example of using the threadpool can be created by using the Task Parallel Library, or TPL.
Listing 11-3. Using the Task Parallel Library
var strings = new List<string>();
for (int i = 0; i < 1000; i++)
{
strings.Add($"Item {i}");
}
Parallel.ForEach(strings, _ =>
{
Console.WriteLine(_); Thread.Sleep(1000);
});
We have a list of 1000 strings. Using the TPL, we can loop over this in a parallel way with Parallel.ForEach. For each item in the list, work will be scheduled on a threadpool thread. The Thread pane in Visual Studio can visualize this.
301
Chapter 11 Advanced .NET 6
Figure 11-3. Visualizing threadpool threads
Running a foreach loop in parallel also means that the order of the outcome can be unpredictable.
Figure 11-4. Parallel ForEach output
A different way to loop over a collection in a parallel manner is using the AsParallel extension method. AsParallel is a method in the LINQ library. It returns a ParallelQuery object. By itself it does not do any parallelization; we need to execute a LINQ query on the ParallelQuery object it returns. Listing 11-4 shows how to use this method.
302
Chapter 11 Advanced .NET 6
Listing 11-4. Using the AsParallel method
var strings = new List<string>();
for (int i = 0; i < 1000; i++)
{
strings.Add($"Item {i}");
}
foreach (string item in strings.AsParallel().Select(_ => _))
{
Console.WriteLine(item);
Thread.Sleep(1000);
}
There is no major difference in using Parallel.ForEach versus AsParallel. The way to use it differs, but the results are similar.
The static ThreadPool class in .NET can tell us how many threadpool workers we can have simultaneously. Listing 11-5 shows how; Figure 11-5 shows the result on my Intel i7 device with 32GB of RAM.
Listing 11-5. Listing ThreadPool information
ThreadPool.GetMaxThreads(out int workerthreads, out int completionports);
Console.WriteLine($"Max number of threads in the threadpool: {workerthreads}");
Console.WriteLine($"Max number of completion ports in the threadpool: {completionports }");
Figure 11-5. Max number of workers in the threadpool
The threadpool consists of two types of threads: worker threads and completion ports. Completion ports are used for handling asynchronous I/O requests. Using completion ports for I/O requests can be much more performant than creating your own
303