.NET HELP

C# Thread Sleep Method (How It Works For Developers)

Published March 6, 2024
Share:

Introduction

Multithreading is a crucial aspect of modern software development, allowing developers to execute multiple tasks concurrently, improving performance and responsiveness. However, managing threads effectively requires careful consideration of synchronization and coordination. One essential tool in a C# developer's arsenal for managing thread timing and coordination is the Thread.Sleep() method.

In this article, we will delve into the intricacies of the Thread.Sleep() method, exploring its purpose, usage, potential pitfalls, and alternatives. Additionally, in this article, we present the IronPDF, C# PDF library by Iron Software, which facilitates the programmatic generation of PDF documents.

Understanding Thread.Sleep()

The Thread.Sleep() method is a part of the System.Threading namespace in C# and is used to block the execution of the current thread for a specified amount of time. The waiting thread or the blocked thread stops the execution until the time specified for sleep. The Sleep method takes a single argument, representing the time interval for which the thread should remain inactive. The argument can be specified in milliseconds or as a TimeSpan object, providing flexibility in expressing the desired pause duration.

// Using Thread.Sleep() with a specified number of milliseconds
Thread.Sleep(1000); // block for 1 second
// Using Thread.Sleep() with TimeSpan
TimeSpan sleepDuration = TimeSpan.FromSeconds(2);
Thread.Sleep(sleepDuration); // block for 2 seconds
// Using Thread.Sleep() with a specified number of milliseconds
Thread.Sleep(1000); // block for 1 second
// Using Thread.Sleep() with TimeSpan
TimeSpan sleepDuration = TimeSpan.FromSeconds(2);
Thread.Sleep(sleepDuration); // block for 2 seconds
' Using Thread.Sleep() with a specified number of milliseconds
Thread.Sleep(1000) ' block for 1 second
' Using Thread.Sleep() with TimeSpan
Dim sleepDuration As TimeSpan = TimeSpan.FromSeconds(2)
Thread.Sleep(sleepDuration) ' block for 2 seconds
VB   C#

Purpose of Thread.Sleep

The primary purpose of using Thread.Sleep is to introduce a delay or pause in the execution of a thread. This can be beneficial in various scenarios, such as:

  1. Simulation of Real-Time Behavior: In scenarios where the application needs to simulate real-time behavior, introducing delays can help mimic the timing constraints of the system being modeled.
  2. Preventing Excessive Resource Consumption: Pausing one thread for a short duration can be useful in scenarios where constant execution is unnecessary, preventing unnecessary resource consumption.
  3. Thread Coordination: When dealing with multiple threads, introducing pauses can help synchronize their execution, preventing race conditions and ensuring orderly processing.

Real World example

Let's consider a real-world example where the Thread.Sleep() method can be employed to simulate a traffic light control system. In this scenario, we'll create a simple console application that models the behavior of a traffic light with red, yellow, and green signals.

using System .Threading;
public class TrafficLightSimulator
{
    static void Main()
    {
        Console.WriteLine("Traffic Light Simulator");
        while (true)
        {
            // Display the red light
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"Stop! Red light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(5000); // Pause for 5 seconds and start execution
            // Display the yellow light
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Get ready! Yellow light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(2000); // Pause for 2 seconds
            // Display the green light
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"Go! Green light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(5000); // Pause for 5 seconds
            // Reset console color
            Console.ResetColor();
            Console.Clear();
        }
    }
}
using System .Threading;
public class TrafficLightSimulator
{
    static void Main()
    {
        Console.WriteLine("Traffic Light Simulator");
        while (true)
        {
            // Display the red light
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"Stop! Red light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(5000); // Pause for 5 seconds and start execution
            // Display the yellow light
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Get ready! Yellow light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(2000); // Pause for 2 seconds
            // Display the green light
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"Go! Green light - {DateTime.Now.ToString("u")}");
            Thread.Sleep(5000); // Pause for 5 seconds
            // Reset console color
            Console.ResetColor();
            Console.Clear();
        }
    }
}
Imports System.Threading
Public Class TrafficLightSimulator
	Shared Sub Main()
		Console.WriteLine("Traffic Light Simulator")
		Do
			' Display the red light
			Console.ForegroundColor = ConsoleColor.Red
			Console.WriteLine($"Stop! Red light - {DateTime.Now.ToString("u")}")
			Thread.Sleep(5000) ' Pause for 5 seconds and start execution
			' Display the yellow light
			Console.ForegroundColor = ConsoleColor.Yellow
			Console.WriteLine($"Get ready! Yellow light - {DateTime.Now.ToString("u")}")
			Thread.Sleep(2000) ' Pause for 2 seconds
			' Display the green light
			Console.ForegroundColor = ConsoleColor.Green
			Console.WriteLine($"Go! Green light - {DateTime.Now.ToString("u")}")
			Thread.Sleep(5000) ' Pause for 5 seconds
			' Reset console color
			Console.ResetColor()
			Console.Clear()
		Loop
	End Sub
End Class
VB   C#

In the above program example, we have a simple traffic light simulation inside a while loop. The Thread.Sleep() method is used to introduce delays between the transitions of the traffic light signals. Here's how the example works:

  1. The program enters an infinite loop to simulate continuous operation.
  2. The red light is displayed for 5 seconds, representing a stop signal.
  3. After 5 seconds, the yellow light is displayed for 2 seconds, indicating a preparation phase.
  4. Finally, the green light is shown for 5 seconds, allowing vehicles to proceed.
  5. The console color is reset, and the loop repeats.

Output

C# Thread Sleep Method (How It Works For Developers): Figure 1 - Program Output: Display the Traffic Light Simulator using Thread.Sleep() method.

This example demonstrates how Thread.Sleep() can be used to control the timing of a traffic light simulation, providing a simple way to model the behavior of a real-world system. Keep in mind that this is a basic example for illustrative purposes, and in a more complex application, you might want to explore more advanced threading and synchronization techniques for handling user input, managing multiple traffic lights, and ensuring accurate timing.

Using Timespan timeout in Sleep method

You can use TimeSpan with the Thread.Sleep() method to specify the sleep duration. Here's an example extending the traffic light simulation from the previous example, using TimeSpan:

using System;
using System.Threading;
class TrafficLightSimulator
{
    public static void Main()
    {
        Console.WriteLine("Traffic Light Simulator");
        while (true)
        {
            // Display the red light
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Stop! Red light- {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(5)); // Pause for 5 seconds
            // Display the yellow light
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("Get ready! Yellow light-     {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(2)); // Pause for 2 seconds
            // Display the green light
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Go! Green light- {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(5)); // Pause for 5 seconds
            // Reset console color
            Console.ResetColor();
            Console.Clear();
        }
    }
}
using System;
using System.Threading;
class TrafficLightSimulator
{
    public static void Main()
    {
        Console.WriteLine("Traffic Light Simulator");
        while (true)
        {
            // Display the red light
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Stop! Red light- {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(5)); // Pause for 5 seconds
            // Display the yellow light
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("Get ready! Yellow light-     {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(2)); // Pause for 2 seconds
            // Display the green light
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Go! Green light- {DateTime.Now.ToString("u")}");
            Thread.Sleep(TimeSpan.FromSeconds(5)); // Pause for 5 seconds
            // Reset console color
            Console.ResetColor();
            Console.Clear();
        }
    }
}
Imports System
Imports System.Threading
Friend Class TrafficLightSimulator
	Public Shared Sub Main()
		Console.WriteLine("Traffic Light Simulator")
		Do
			' Display the red light
			Console.ForegroundColor = ConsoleColor.Red
			Console.WriteLine("Stop! Red light- {DateTime.Now.ToString("u")}")
			Thread.Sleep(TimeSpan.FromSeconds(5)) ' Pause for 5 seconds
			' Display the yellow light
			Console.ForegroundColor = ConsoleColor.Yellow
			Console.WriteLine("Get ready! Yellow light-     {DateTime.Now.ToString("u")}")
			Thread.Sleep(TimeSpan.FromSeconds(2)) ' Pause for 2 seconds
			' Display the green light
			Console.ForegroundColor = ConsoleColor.Green
			Console.WriteLine("Go! Green light- {DateTime.Now.ToString("u")}")
			Thread.Sleep(TimeSpan.FromSeconds(5)) ' Pause for 5 seconds
			' Reset console color
			Console.ResetColor()
			Console.Clear()
		Loop
	End Sub
End Class
VB   C#

In this modified example, TimeSpan.FromSeconds() is used to create a TimeSpan object representing the desired sleep duration. This makes the code more readable and expressive.

By using TimeSpan property in the Thread.Sleep() method, you can directly specify the duration in seconds (or any other unit supported by TimeSpan), providing a more intuitive way to work with time intervals. This can be especially useful when dealing with longer or more complex sleep durations in your application.

Use Cases

  1. Simulating Real-Time Behavior: Consider a simulation application where you need to model the behavior of a real-time system. By strategically placing Thread.Sleep() in your code, you can mimic the time delays that occur in the actual system, enhancing the accuracy of your simulation.
// Simulating real-time behavior with Thread.Sleep()
SimulateRealTimeEvent();
Thread.Sleep(1000); // Pause for 1 second
SimulateNextEvent();
// Simulating real-time behavior with Thread.Sleep()
SimulateRealTimeEvent();
Thread.Sleep(1000); // Pause for 1 second
SimulateNextEvent();
' Simulating real-time behavior with Thread.Sleep()
SimulateRealTimeEvent()
Thread.Sleep(1000) ' Pause for 1 second
SimulateNextEvent()
VB   C#
  1. Animation and UI Updates: In graphical web development applications or game development, smooth animations and UI updates are crucial. Thread.Sleep() can be used to control the frame rate and ensure that updates occur at a visually pleasing pace.
// Updating UI with controlled delays
UpdateUIElement();
Thread.Sleep(50); // Pause for 50 milliseconds
UpdateNextUIElement();
// Updating UI with controlled delays
UpdateUIElement();
Thread.Sleep(50); // Pause for 50 milliseconds
UpdateNextUIElement();
' Updating UI with controlled delays
UpdateUIElement()
Thread.Sleep(50) ' Pause for 50 milliseconds
UpdateNextUIElement()
VB   C#
  1. Throttling External Service Calls: When interacting with external services or APIs, it's common to impose rate limits or throttling to prevent excessive requests. Thread.Sleep() can be employed to introduce delays between consecutive service calls, staying within rate limits.
// Throttling service calls with Thread.Sleep()
CallExternalService();
Thread.Sleep(2000); // Pause for 2 seconds before the next call
CallNextService();
// Throttling service calls with Thread.Sleep()
CallExternalService();
Thread.Sleep(2000); // Pause for 2 seconds before the next call
CallNextService();
' Throttling service calls with Thread.Sleep()
CallExternalService()
Thread.Sleep(2000) ' Pause for 2 seconds before the next call
CallNextService()
VB   C#

Benefits of Thread.Sleep()

  1. Synchronization and Coordination: Thread.Sleep() aids in synchronizing thread execution, preventing race conditions and ensuring orderly processing when dealing with multiple threads.
  2. Resource Conservation: Pausing a thread temporarily can be advantageous in scenarios where constant execution is unnecessary, conserving system resources.
  3. Simplicity and Readability: The method provides a simple and readable way to introduce delays, making code more understandable, especially for developers new to multithreading concepts.

Potential Pitfalls and Considerations

While Thread.Sleep() is a straightforward solution for introducing delays, there are potential pitfalls and considerations that developers should be aware of:

  1. Blocking the Thread: When a thread is paused using Thread.Sleep(), it is effectively blocked, and no other work can be performed during that time. In scenarios where responsiveness is critical, blocking the main thread for extended periods can lead to a poor user experience.
  2. Inaccuracy in Timing: The accuracy of the pause duration is subject to the underlying operating system's scheduling and may not be precise. Developers should be cautious when relying on Thread.Sleep() for precise timing requirements.
  3. Alternative Approaches: In modern C# development, alternatives like the Task.Delay() method or asynchronous programming using async/await are often preferred over Thread.Sleep(). These approaches provide better responsiveness without blocking threads.
// Using Task.Delay() instead of Thread.Sleep()
await Task.Delay(1000); // Pause for 1 second asynchronously
// Using Task.Delay() instead of Thread.Sleep()
await Task.Delay(1000); // Pause for 1 second asynchronously
' Using Task.Delay() instead of Thread.Sleep()
Await Task.Delay(1000) ' Pause for 1 second asynchronously
VB   C#

Introducing IronPDF

IronPDF, developed by Iron Software, is a C# PDF library serving as both a PDF generator and reader. This section introduces fundamental functionality. For further details, consult the documentation page.

The highlight of IronPDF is its HTML to PDF feature, ensuring all layouts and styles are preserved. It turns web content into PDFs, useful for reports, invoices, and documentation. HTML files, URLs, and HTML strings can be easily converted into PDFs.

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
VB   C#

Installation

To install IronPDF, utilize either the NuGet package manager console or the Visual Studio package manager.

Install IronPDF library using NuGet package manager console using one of the following commands:

dotnet add package IronPdf
# or
Install-Package IronPdf

Install IronPDF library using Visual Studio's Package Manager:

C# Thread Sleep Method (How It Works For Developers): Figure 2 - Install IronPDF using NuGet Package Manager by searching "ironpdf" in the search bar of NuGet Package Manager.

using System;
using IronPdf;
class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public void DisplayFullName()
    {
        if (string.IsNullOrEmpty(FirstName) || string.IsNullOrEmpty(LastName))
        {
            LogError($"Invalid name: {nameof(FirstName)} or {nameof(LastName)} is missing.");
        }
        else
        {
            Console.WriteLine($"Full Name: {FirstName} {LastName}");
        }
    }
    public void PrintPdf()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        string content = $@"<!DOCTYPE html>
<html>
<body>
<h1>Hello, {FirstName}!</h1>
<p>First Name: {FirstName}</p>
<p>First Name: {LastName}</p>
</body>
</html>";
        // Create a new PDF document
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("person.pdf");
    }
    private void LogError(string errorMessage)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine($"Error: {errorMessage}");
        Console.ResetColor();
    }
}
class Program
{
    public static void Main()
    {
        // Create an  instance of the Person class
        Person person = new Person();
        // Attempt to display the full name
        person.DisplayFullName();
        // Set the properties
        person.FirstName = "John"; // string literal
        person.LastName = "Doe"; // string literal
        // Display the full name again
        person.DisplayFullName();
        Console.WriteLine("Pause for 2 seconds and Print PDF");
        Thread.Sleep(2000); // Pause for 2 seconds and Print PDF
        // Print the full name to PDF
        person.PrintPdf();
    }
}
using System;
using IronPdf;
class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public void DisplayFullName()
    {
        if (string.IsNullOrEmpty(FirstName) || string.IsNullOrEmpty(LastName))
        {
            LogError($"Invalid name: {nameof(FirstName)} or {nameof(LastName)} is missing.");
        }
        else
        {
            Console.WriteLine($"Full Name: {FirstName} {LastName}");
        }
    }
    public void PrintPdf()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        string content = $@"<!DOCTYPE html>
<html>
<body>
<h1>Hello, {FirstName}!</h1>
<p>First Name: {FirstName}</p>
<p>First Name: {LastName}</p>
</body>
</html>";
        // Create a new PDF document
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("person.pdf");
    }
    private void LogError(string errorMessage)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine($"Error: {errorMessage}");
        Console.ResetColor();
    }
}
class Program
{
    public static void Main()
    {
        // Create an  instance of the Person class
        Person person = new Person();
        // Attempt to display the full name
        person.DisplayFullName();
        // Set the properties
        person.FirstName = "John"; // string literal
        person.LastName = "Doe"; // string literal
        // Display the full name again
        person.DisplayFullName();
        Console.WriteLine("Pause for 2 seconds and Print PDF");
        Thread.Sleep(2000); // Pause for 2 seconds and Print PDF
        // Print the full name to PDF
        person.PrintPdf();
    }
}
Imports System
Imports IronPdf
Friend Class Person
	Public Property FirstName() As String
	Public Property LastName() As String
	Public Sub DisplayFullName()
		If String.IsNullOrEmpty(FirstName) OrElse String.IsNullOrEmpty(LastName) Then
			LogError($"Invalid name: {NameOf(FirstName)} or {NameOf(LastName)} is missing.")
		Else
			Console.WriteLine($"Full Name: {FirstName} {LastName}")
		End If
	End Sub
	Public Sub PrintPdf()
		Console.WriteLine("Generating PDF using IronPDF.")
		Dim content As String = $"<!DOCTYPE html>
<html>
<body>
<h1>Hello, {FirstName}!</h1>
<p>First Name: {FirstName}</p>
<p>First Name: {LastName}</p>
</body>
</html>"
		' Create a new PDF document
		Dim pdfDocument = New ChromePdfRenderer()
		pdfDocument.RenderHtmlAsPdf(content).SaveAs("person.pdf")
	End Sub
	Private Sub LogError(ByVal errorMessage As String)
		Console.ForegroundColor = ConsoleColor.Red
		Console.WriteLine($"Error: {errorMessage}")
		Console.ResetColor()
	End Sub
End Class
Friend Class Program
	Public Shared Sub Main()
		' Create an  instance of the Person class
		Dim person As New Person()
		' Attempt to display the full name
		person.DisplayFullName()
		' Set the properties
		person.FirstName = "John" ' string literal
		person.LastName = "Doe" ' string literal
		' Display the full name again
		person.DisplayFullName()
		Console.WriteLine("Pause for 2 seconds and Print PDF")
		Thread.Sleep(2000) ' Pause for 2 seconds and Print PDF
		' Print the full name to PDF
		person.PrintPdf()
	End Sub
End Class
VB   C#

In this program, we demonstrate how to use Thread.Sleep and IronPDF. The code initially validates for a person's firstname and lastname properties. Then prints the full name of the person on the console. Then waits for 2 seconds using Thread.Sleep and later prints the fullname to PDF using the PrintPdf() method and IronPDF library.

Output

C# Thread Sleep Method (How It Works For Developers): Figure 3 - Console Output: Displaying the use of Thread.Sleep in PDF generation using IronPDF.

Generated PDF

C# Thread Sleep Method (How It Works For Developers): Figure 4 - Output PDF created.

Licensing (Free Trial Available)

To use IronPDF. Insert this key into the appsettings.json file.

"IronPdf.LicenseKey": "your license key"
"IronPdf.LicenseKey": "your license key"
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'"IronPdf.LicenseKey": "your license key"
VB   C#

To receive a trial license, please provide your email. For more information on IronPDF's licensing, please visit this license page.

Conclusion

The Thread.Sleep() method in C# serves as a fundamental tool for managing thread timing and synchronization. While it is a simple and effective solution for introducing delays, developers should be mindful of its limitations and potential impact on application performance. As modern C# development evolves, exploring alternative approaches like Task.Delay() and asynchronous programming becomes essential for writing responsive and efficient multithreaded applications. By understanding the nuances of thread synchronization and selecting the appropriate tools, developers can create robust and efficient software that meets the demands of concurrent processing in a dynamic environment.

Furthermore, we observed the versatility of the IronPDF library in the generation of PDF documents and how it can be used with the Thread.Sleep method. For more examples on how to use IronPDF, please visit their code examples page.

< PREVIOUS
C# Null Conditional Operator (How It Works For Developers)
NEXT >
C# Const (How It Works For Developers)

Ready to get started? Version: 2024.10 just released

Free NuGet Download Total downloads: 11,308,499 View Licenses >