.NET 帮助

C# Semaphoreslim(如何为开发人员工作)

Chipego
奇佩戈-卡琳达
2024年十月23日
分享:

介绍

并发管理是 C# 中高性能应用程序的一个重要方面。 它可以确保资源得到有效利用,同时避免潜在的冲突或性能瓶颈,因此有一个轻量级的信号控制访问会非常有帮助。 这就是 SemaphoreSlim 发挥作用的地方。 SemaphoreSlim 是一种轻量级同步原语,用于控制资源访问,最终防止出现竞赛条件并确保线程安全。

那么,如果您想将其与管理 PDF 生成流程的 PDF 库一起实施呢? 您可能正在寻找一个强大的PDF库,这时IronPDF就派上用场了。 IronPDF 是面向 .NET 开发人员的强大的 PDF 生成和处理库,在多线程环境中使用时可极大地受益于并发管理。

如果您想了解SemaphoreSlim和IronPDF的实际应用,请务必继续阅读,我们将探讨使用SemaphoreSlim的好处,以及如何将其与IronPDF集成,以安全地处理并发操作、提高性能并确保可靠的PDF处理。

理解 C# 中的 SemaphoreSlim;

什么是 SemaphoreSlim?

SemaphoreSlim 是 .NET 中的一种同步原语,用于限制可以并发访问特定资源或资源池的线程数量。 它是完整 Semaphore 类的轻量级版本,设计用于在只需使用更简单、更快速的 Semaphore 的情况下更高效地工作。

使用 SemaphoreSlim 的一些好处是,与 Semaphore 相比,系统开销减少了,它非常适合管理有限的资源(例如数据库连接或文件访问),并支持异步等待方法,使其非常适合现代的 async/await 编程模式。

SemaphoreSlim基本用法代码示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    // Semaphore count
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(3); // Limit to 3 concurrent threads.

    static async Task Main(string[] args)
    {
        // Start tasks that will wait on the semaphore.
        var tasks = new Task[5];

        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() => AccessResource(i));
        }

        // Simulate some work in the main thread (e.g., initialization).
        Console.WriteLine("Main thread is preparing resources...");
        await Task.Delay(2000);  // Simulate initialization delay.

        // Main thread calls release, releases semaphore permits to allow waiting tasks to proceed.
        Console.WriteLine("Main thread releasing semaphore permits...");
        _semaphore.Release(2);  // Releases 2 permits, allowing up to 2 tasks to proceed.

        // Wait for all tasks to complete.
        await Task.WhenAll(tasks);
        Console.WriteLine("All tasks completed.");
    }

    static async Task AccessResource(int id)
    {
        Console.WriteLine($"Task {id} waiting to enter...");
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Current thread successfully entered by Task {id}.");
            await Task.Delay(1000); // Simulate work.
        }
        finally
        {
            Console.WriteLine($"Task {id} releasing.");
            _semaphore.Release();
        }
    }
}
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    // Semaphore count
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(3); // Limit to 3 concurrent threads.

    static async Task Main(string[] args)
    {
        // Start tasks that will wait on the semaphore.
        var tasks = new Task[5];

        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() => AccessResource(i));
        }

        // Simulate some work in the main thread (e.g., initialization).
        Console.WriteLine("Main thread is preparing resources...");
        await Task.Delay(2000);  // Simulate initialization delay.

        // Main thread calls release, releases semaphore permits to allow waiting tasks to proceed.
        Console.WriteLine("Main thread releasing semaphore permits...");
        _semaphore.Release(2);  // Releases 2 permits, allowing up to 2 tasks to proceed.

        // Wait for all tasks to complete.
        await Task.WhenAll(tasks);
        Console.WriteLine("All tasks completed.");
    }

    static async Task AccessResource(int id)
    {
        Console.WriteLine($"Task {id} waiting to enter...");
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Current thread successfully entered by Task {id}.");
            await Task.Delay(1000); // Simulate work.
        }
        finally
        {
            Console.WriteLine($"Task {id} releasing.");
            _semaphore.Release();
        }
    }
}
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Friend Class Program
	' Semaphore count
	Private Shared _semaphore As New SemaphoreSlim(3) ' Limit to 3 concurrent threads.

	Shared Async Function Main(ByVal args() As String) As Task
		' Start tasks that will wait on the semaphore.
		Dim tasks = New Task(4){}

		For i As Integer = 0 To tasks.Length - 1
			tasks(i) = Task.Run(Function() AccessResource(i))
		Next i

		' Simulate some work in the main thread (e.g., initialization).
		Console.WriteLine("Main thread is preparing resources...")
		Await Task.Delay(2000) ' Simulate initialization delay.

		' Main thread calls release, releases semaphore permits to allow waiting tasks to proceed.
		Console.WriteLine("Main thread releasing semaphore permits...")
		_semaphore.Release(2) ' Releases 2 permits, allowing up to 2 tasks to proceed.

		' Wait for all tasks to complete.
		Await Task.WhenAll(tasks)
		Console.WriteLine("All tasks completed.")
	End Function

	Private Shared Async Function AccessResource(ByVal id As Integer) As Task
		Console.WriteLine($"Task {id} waiting to enter...")
		Await _semaphore.WaitAsync()

		Try
			Console.WriteLine($"Current thread successfully entered by Task {id}.")
			Await Task.Delay(1000) ' Simulate work.
		Finally
			Console.WriteLine($"Task {id} releasing.")
			_semaphore.Release()
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

在程序运行过程中,当所有可用许可都已被线程获取时,线程的 Semaphore 计数可能会动态为零。 此状态表示已达到允许的最大并发访问量。

如果您愿意,可以设置线程的初始数目和最大数目,初始信号量计数从零开始,然后使用一个单独的初始化任务在资源就绪时增加信号量计数,从而允许您选择的线程数继续进行。 当信号量计数为零时,线程将会在尝试进入信号量时等待,这被称为“阻塞等待”。

您可以跟踪先前的信号量计数,以根据先前的计数调整信号量的行为。 然后您可以相应地操作信号量(例如,通过释放或等待)。 随着线程的释放,semaphore 的数量也会减少。

控制台输出

C# Semaphoreslim(它对开发人员的工作原理): 图 1

SemaphoreSlim 的常见使用案例

SemaphoreSlim 的一些常见使用案例包括

  • 限制对数据库或文件系统的访问: 这可以防止这些资源被过多的并发请求压垮。
  • 管理线程池:它可以用来控制执行特定操作的线程数量,从而提高稳定性和性能。

使用 SemaphoreSlim 和 IronPDF 实现安全并发

在多线程环境中设置 IronPDF

要在多线程环境中开始使用IronPDF,请先安装IronPDF NuGet包。 您可以通过导航到工具 > NuGet包管理器 > 解决方案的NuGet包管理器并搜索IronPDF来完成此操作:

C# Semaphoreslim(开发人员如何工作):图 2

或者,在包管理器控制台中运行以下命令:

Install-Package IronPdf

要在代码中开始使用IronPDF,请确保在代码文件的顶部放置using IronPdf语句。要了解有关在环境中设置IronPDF的更深入指南,请查看其入门页面。

使用 SemaphoreSlim 控制 PDF 生成的访问权限

当您使用 SemaphoreSlim 时,您可以有效地控制对 PDF 生成任务的访问。 这可确保您的应用程序不会试图同时生成过多的 PDF,以免影响性能或导致故障。

以下示例代码演示了 SemaphoreSlim 与 IronPDF 的基本用法。

using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(2); // Limit to 2 concurrent threads.

    static async Task Main(string[] args)
    {
        var tasks = new Task[5];

        for (int i = 0; i < tasks.Length; i++)
        {
            string htmlContent = $"<h1>PDF Document {i}</h1><p>This is a sample PDF content for task {i}.</p>";
            string outputPath = $"output_{i}.pdf";

            // Start multiple tasks to demonstrate controlled concurrency.
            tasks[i] = GeneratePdfAsync(htmlContent, outputPath, i);
        }

        await Task.WhenAll(tasks);
    }

    static async Task GeneratePdfAsync(string htmlContent, string outputPath, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting for access...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} has started PDF generation.");
            ChromePdfRenderer renderer = new ChromePdfRenderer();
            PdfDocument pdf = await renderer.RenderHtmlAsPdfAsync(htmlContent);
            pdf.SaveAs(outputPath);
            Console.WriteLine($"Task {taskId} has completed PDF generation.");
        }
        finally
        {
            // Ensure semaphore is released to allow other tasks to proceed.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(2); // Limit to 2 concurrent threads.

    static async Task Main(string[] args)
    {
        var tasks = new Task[5];

        for (int i = 0; i < tasks.Length; i++)
        {
            string htmlContent = $"<h1>PDF Document {i}</h1><p>This is a sample PDF content for task {i}.</p>";
            string outputPath = $"output_{i}.pdf";

            // Start multiple tasks to demonstrate controlled concurrency.
            tasks[i] = GeneratePdfAsync(htmlContent, outputPath, i);
        }

        await Task.WhenAll(tasks);
    }

    static async Task GeneratePdfAsync(string htmlContent, string outputPath, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting for access...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} has started PDF generation.");
            ChromePdfRenderer renderer = new ChromePdfRenderer();
            PdfDocument pdf = await renderer.RenderHtmlAsPdfAsync(htmlContent);
            pdf.SaveAs(outputPath);
            Console.WriteLine($"Task {taskId} has completed PDF generation.");
        }
        finally
        {
            // Ensure semaphore is released to allow other tasks to proceed.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
Imports IronPdf
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Friend Class Program
	Private Shared _semaphore As New SemaphoreSlim(2) ' Limit to 2 concurrent threads.

	Shared Async Function Main(ByVal args() As String) As Task
		Dim tasks = New Task(4){}

		For i As Integer = 0 To tasks.Length - 1
			Dim htmlContent As String = $"<h1>PDF Document {i}</h1><p>This is a sample PDF content for task {i}.</p>"
			Dim outputPath As String = $"output_{i}.pdf"

			' Start multiple tasks to demonstrate controlled concurrency.
			tasks(i) = GeneratePdfAsync(htmlContent, outputPath, i)
		Next i

		Await Task.WhenAll(tasks)
	End Function

	Private Shared Async Function GeneratePdfAsync(ByVal htmlContent As String, ByVal outputPath As String, ByVal taskId As Integer) As Task
		Console.WriteLine($"Task {taskId} is waiting for access...")

		' Wait to enter the semaphore.
		Await _semaphore.WaitAsync()

		Try
			Console.WriteLine($"Task {taskId} has started PDF generation.")
			Dim renderer As New ChromePdfRenderer()
			Dim pdf As PdfDocument = Await renderer.RenderHtmlAsPdfAsync(htmlContent)
			pdf.SaveAs(outputPath)
			Console.WriteLine($"Task {taskId} has completed PDF generation.")
		Finally
			' Ensure semaphore is released to allow other tasks to proceed.
			_semaphore.Release()
			Console.WriteLine($"Task {taskId} has released semaphore.")
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

在本示例中,我们首先初始化了SemaphoreSlim,并将SemaphoreSlim的初始和最大计数设置为 "2",限制其只能并发生成两份PDF文件。 然后,我们创建了一个任务数组,用于控制程序必须完成的任务数量,之后我们使用一个 for 循环,根据任务数组中的任务数量动态创建 PDF。

然后使用WaitAsync()方法进入信号量,并在finally块中使用Release()以确保即使发生异常也始终释放信号量。 控制台输出日志会显示每个任务开始、完成和释放信号的时间,这样您就可以跟踪并发行为。

输出控制台

C# Semaphoreslim(开发者的工作原理):图3

输出 PDF 文件

C# SemaphoreSlim(开发人员使用方法):图 4

确保 PDF 操作任务中的线程安全

当多个线程与共享资源交互时,线程安全至关重要。 在 PDF 操作中,SemaphoreSlim 可确保只有规定数量的线程可以同时修改 PDF,从而防止出现竞赛条件并确保一致性。 在下面的代码中,我们模拟了一个场景,即在多个 PDF 上添加水印,同时确保一次只进行一个操作。

using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);

    static async Task Main(string[] args)
    {
        // Setting array of tasks
        var tasks = new Task[3];

        for (int i = 0; i < tasks.Length; i++)
        {
            string inputPath = $"input_{i}.pdf";  // Input PDF file path
            string outputPath = $"output_{i}.pdf";  // Output PDF file path
            string watermarkText = @"
<img src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>";

            // Start multiple tasks to add watermarks concurrently.
            tasks[i] = AddWatermarkAsync(inputPath, outputPath, watermarkText, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    static async Task AddWatermarkAsync(string input, string outputPath, string watermark, int taskId)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is waiting to add a watermark...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is adding a watermark.");
            var pdf = PdfDocument.FromFile(input);
            pdf.ApplyWatermark(watermark); // Add watermark
            pdf.SaveAs(outputPath); // Save the modified PDF
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has completed watermarking.");
        }
        finally
        {
            // Release the semaphore after the task is done.
            _semaphore.Release();
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has released semaphore.");
        }
    }
}
using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);

    static async Task Main(string[] args)
    {
        // Setting array of tasks
        var tasks = new Task[3];

        for (int i = 0; i < tasks.Length; i++)
        {
            string inputPath = $"input_{i}.pdf";  // Input PDF file path
            string outputPath = $"output_{i}.pdf";  // Output PDF file path
            string watermarkText = @"
<img src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>";

            // Start multiple tasks to add watermarks concurrently.
            tasks[i] = AddWatermarkAsync(inputPath, outputPath, watermarkText, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    static async Task AddWatermarkAsync(string input, string outputPath, string watermark, int taskId)
    {
        Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is waiting to add a watermark...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is adding a watermark.");
            var pdf = PdfDocument.FromFile(input);
            pdf.ApplyWatermark(watermark); // Add watermark
            pdf.SaveAs(outputPath); // Save the modified PDF
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has completed watermarking.");
        }
        finally
        {
            // Release the semaphore after the task is done.
            _semaphore.Release();
            Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has released semaphore.");
        }
    }
}
Imports IronPdf
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Friend Class Program
	Private Shared _semaphore As New SemaphoreSlim(1)

	Shared Async Function Main(ByVal args() As String) As Task
		' Setting array of tasks
		Dim tasks = New Task(2){}

		For i As Integer = 0 To tasks.Length - 1
			Dim inputPath As String = $"input_{i}.pdf" ' Input PDF file path
			Dim outputPath As String = $"output_{i}.pdf" ' Output PDF file path
			Dim watermarkText As String = "
<img src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>"

			' Start multiple tasks to add watermarks concurrently.
			tasks(i) = AddWatermarkAsync(inputPath, outputPath, watermarkText, i)
		Next i

		Await Task.WhenAll(tasks) ' Wait for all tasks to finish.
	End Function

	Private Shared Async Function AddWatermarkAsync(ByVal input As String, ByVal outputPath As String, ByVal watermark As String, ByVal taskId As Integer) As Task
		Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is waiting to add a watermark...")

		' Wait to enter the semaphore.
		Await _semaphore.WaitAsync()

		Try
			Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} is adding a watermark.")
			Dim pdf = PdfDocument.FromFile(input)
			pdf.ApplyWatermark(watermark) ' Add watermark
			pdf.SaveAs(outputPath) ' Save the modified PDF
			Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has completed watermarking.")
		Finally
			' Release the semaphore after the task is done.
			_semaphore.Release()
			Console.WriteLine($"{DateTime.Now:HH:mm:ss} - Task {taskId} has released semaphore.")
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

通过使用private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);将信号量计数设置为1,我们确保一次只有一个任务可以操作PDF。

控制台输出

C# Semaphoreslim(它如何为开发人员工作):图 5

使用 SemaphoreSlim 和 IronPDF 优化性能

管理资源密集型业务

IronPDF 擅长处理资源密集型任务,如将大型 HTML 文件转换为 PDF,并擅长在异步环境中执行这些任务。 使用 SemaphoreSlim 来管理这些操作可确保您的应用程序即使在高负载情况下也能保持响应速度,而不会降低性能。

下面的示例代码演示了一种情况,我们需要限制并发的大型 HTML 到 PDF 转换的数量,以避免系统资源超载。

using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    // Limit concurrent large PDF conversions to 2.
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(2);

    static async Task Main(string[] args)
    {
        var tasks = new Task[4];

        for (int i = 0; i < tasks.Length; i++)
        {
            string htmlContent = $"<h1>Large Document {i}</h1><p>Content for a large HTML file {i}.</p>";
            string outputPath = $"large_output_{i}.pdf";

            // Start multiple tasks to convert large HTML files to PDFs.
            tasks[i] = ConvertLargeHtmlAsync(htmlContent, outputPath, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    // Method to convert large HTML to PDF using SemaphoreSlim to control resource usage.
    public static async Task ConvertLargeHtmlAsync(string htmlContent, string outputPath, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting to start conversion...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} is converting large HTML to PDF.");
            var renderer = new ChromePdfRenderer();
            var pdf = await renderer.RenderHtmlAsPdfAsync(htmlContent); // Convert large HTML to PDF
            pdf.SaveAs(outputPath); // Save the PDF file
            Console.WriteLine($"Task {taskId} has completed conversion.");
        }
        finally
        {
            // Ensure the semaphore is released to allow other tasks to proceed.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    // Limit concurrent large PDF conversions to 2.
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(2);

    static async Task Main(string[] args)
    {
        var tasks = new Task[4];

        for (int i = 0; i < tasks.Length; i++)
        {
            string htmlContent = $"<h1>Large Document {i}</h1><p>Content for a large HTML file {i}.</p>";
            string outputPath = $"large_output_{i}.pdf";

            // Start multiple tasks to convert large HTML files to PDFs.
            tasks[i] = ConvertLargeHtmlAsync(htmlContent, outputPath, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    // Method to convert large HTML to PDF using SemaphoreSlim to control resource usage.
    public static async Task ConvertLargeHtmlAsync(string htmlContent, string outputPath, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting to start conversion...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} is converting large HTML to PDF.");
            var renderer = new ChromePdfRenderer();
            var pdf = await renderer.RenderHtmlAsPdfAsync(htmlContent); // Convert large HTML to PDF
            pdf.SaveAs(outputPath); // Save the PDF file
            Console.WriteLine($"Task {taskId} has completed conversion.");
        }
        finally
        {
            // Ensure the semaphore is released to allow other tasks to proceed.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
Imports IronPdf
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Friend Class Program
	' Limit concurrent large PDF conversions to 2.
	Private Shared _semaphore As New SemaphoreSlim(2)

	Shared Async Function Main(ByVal args() As String) As Task
		Dim tasks = New Task(3){}

		For i As Integer = 0 To tasks.Length - 1
			Dim htmlContent As String = $"<h1>Large Document {i}</h1><p>Content for a large HTML file {i}.</p>"
			Dim outputPath As String = $"large_output_{i}.pdf"

			' Start multiple tasks to convert large HTML files to PDFs.
			tasks(i) = ConvertLargeHtmlAsync(htmlContent, outputPath, i)
		Next i

		Await Task.WhenAll(tasks) ' Wait for all tasks to finish.
	End Function

	' Method to convert large HTML to PDF using SemaphoreSlim to control resource usage.
	Public Shared Async Function ConvertLargeHtmlAsync(ByVal htmlContent As String, ByVal outputPath As String, ByVal taskId As Integer) As Task
		Console.WriteLine($"Task {taskId} is waiting to start conversion...")

		' Wait to enter the semaphore.
		Await _semaphore.WaitAsync()

		Try
			Console.WriteLine($"Task {taskId} is converting large HTML to PDF.")
			Dim renderer = New ChromePdfRenderer()
			Dim pdf = Await renderer.RenderHtmlAsPdfAsync(htmlContent) ' Convert large HTML to PDF
			pdf.SaveAs(outputPath) ' Save the PDF file
			Console.WriteLine($"Task {taskId} has completed conversion.")
		Finally
			' Ensure the semaphore is released to allow other tasks to proceed.
			_semaphore.Release()
			Console.WriteLine($"Task {taskId} has released semaphore.")
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

在处理将大型 HTML 文件转换为 PDF 等资源密集型任务时,SemaphoreSlim 可帮助平衡负载并优化资源使用。 通过设置 2 个并发操作的限制,我们可以防止系统被资源密集型 PDF 生成任务压垮。 这种方法有助于更均匀地分配工作量,提高应用程序的整体性能和稳定性。

输出图像:使用此方法生成的文件

C# Semaphoreslim(它如何为开发人员工作):图6

避免并发管理中的死锁

如果不能正确释放 Semaphores,就会出现死锁。 需要牢记的一个良好做法是使用 try-finally 块,以确保即使出现异常也能释放 semaphores,从而防止死锁并保持应用程序的流畅运行。 为避免死锁,记得一些最佳实践,包括始终在 finally 块中释放信号量,并避免在异步代码中使用类似 Wait()Result 的阻塞调用。

using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(3);

    static async Task Main(string[] args)
    {
        var tasks = new Task[3];

        for (int i = 0; i < tasks.Length; i++)
        {
            string content = $"<h1>Document {i}</h1><p>Content for PDF {i}.</p>";
            string path = $"safe_output_{i}.pdf";

            // Start multiple tasks to demonstrate deadlock-free semaphore usage.
            tasks[i] = SafePdfTaskAsync(content, path, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    // Method demonstrating best practices for using SemaphoreSlim to avoid deadlocks.
    public static async Task SafePdfTaskAsync(string content, string path, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting to generate PDF...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} is generating PDF.");
            var renderer = new ChromePdfRenderer();
            var pdf = await renderer.RenderHtmlAsPdfAsync(content); // Render HTML to PDF
            pdf.SaveAs(path); // Save the PDF
            Console.WriteLine($"Task {taskId} has completed PDF generation.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Task {taskId} encountered an error: {ex.Message}");
        }
        finally
        {
            // Always release the semaphore, even if an error occurs.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
using IronPdf;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim _semaphore = new SemaphoreSlim(3);

    static async Task Main(string[] args)
    {
        var tasks = new Task[3];

        for (int i = 0; i < tasks.Length; i++)
        {
            string content = $"<h1>Document {i}</h1><p>Content for PDF {i}.</p>";
            string path = $"safe_output_{i}.pdf";

            // Start multiple tasks to demonstrate deadlock-free semaphore usage.
            tasks[i] = SafePdfTaskAsync(content, path, i);
        }

        await Task.WhenAll(tasks); // Wait for all tasks to finish.
    }

    // Method demonstrating best practices for using SemaphoreSlim to avoid deadlocks.
    public static async Task SafePdfTaskAsync(string content, string path, int taskId)
    {
        Console.WriteLine($"Task {taskId} is waiting to generate PDF...");

        // Wait to enter the semaphore.
        await _semaphore.WaitAsync();

        try
        {
            Console.WriteLine($"Task {taskId} is generating PDF.");
            var renderer = new ChromePdfRenderer();
            var pdf = await renderer.RenderHtmlAsPdfAsync(content); // Render HTML to PDF
            pdf.SaveAs(path); // Save the PDF
            Console.WriteLine($"Task {taskId} has completed PDF generation.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Task {taskId} encountered an error: {ex.Message}");
        }
        finally
        {
            // Always release the semaphore, even if an error occurs.
            _semaphore.Release();
            Console.WriteLine($"Task {taskId} has released semaphore.");
        }
    }
}
Imports IronPdf
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Friend Class Program
	Private Shared _semaphore As New SemaphoreSlim(3)

	Shared Async Function Main(ByVal args() As String) As Task
		Dim tasks = New Task(2){}

		For i As Integer = 0 To tasks.Length - 1
			Dim content As String = $"<h1>Document {i}</h1><p>Content for PDF {i}.</p>"
			Dim path As String = $"safe_output_{i}.pdf"

			' Start multiple tasks to demonstrate deadlock-free semaphore usage.
			tasks(i) = SafePdfTaskAsync(content, path, i)
		Next i

		Await Task.WhenAll(tasks) ' Wait for all tasks to finish.
	End Function

	' Method demonstrating best practices for using SemaphoreSlim to avoid deadlocks.
	Public Shared Async Function SafePdfTaskAsync(ByVal content As String, ByVal path As String, ByVal taskId As Integer) As Task
		Console.WriteLine($"Task {taskId} is waiting to generate PDF...")

		' Wait to enter the semaphore.
		Await _semaphore.WaitAsync()

		Try
			Console.WriteLine($"Task {taskId} is generating PDF.")
			Dim renderer = New ChromePdfRenderer()
			Dim pdf = Await renderer.RenderHtmlAsPdfAsync(content) ' Render HTML to PDF
			pdf.SaveAs(path) ' Save the PDF
			Console.WriteLine($"Task {taskId} has completed PDF generation.")
		Catch ex As Exception
			Console.WriteLine($"Task {taskId} encountered an error: {ex.Message}")
		Finally
			' Always release the semaphore, even if an error occurs.
			_semaphore.Release()
			Console.WriteLine($"Task {taskId} has released semaphore.")
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

通过使用try-catch-finally块,我们确保了即使抛出异常,也始终释放SemaphoreSlim对象,从而防止死锁。 通过记录错误和正确管理信号释放,我们可以保持程序稳定,防止出现任何意外行为。

正如您在下面的输出图像中看到的,我通过尝试让程序加载一个不存在的 HTML 文件来模拟错误,但即使出现了这样的错误,程序也会打印错误信息,告诉我出了什么问题,然后继续使用 finally 块释放信号。

C# Semaphoreslim(它对开发人员的工作原理):图 7

使用 IronPDF 进行并发 PDF 处理的优势

高效可靠的 PDF 处理

IronPDF 设计用于高效处理并发 PDF 处理任务,其性能和可靠性优于许多其他 PDF 库。 其强大的架构可根据应用程序的需求进行扩展,是高需求环境的理想选择。 在性能、易用性和稳健性等标准上进行比较时,IronPDF 被证明是一个强大的竞争者。 为了展示这一点,我将 IronPDF 与其他几个流行的 PDF 库进行了比较,如 iTextSharp、PDFsharp、DinkToPdf 和 EvoPDF:

性能

IronPDF:

  • 渲染速度:IronPDF以其快速高效的渲染能力而闻名,尤其是在将HTML转换为PDF时。 它使用了基于 Chrome 浏览器的渲染技术,可以高保真地呈现原始 HTML 内容,包括 CSS 和 JavaScript 的执行。
  • 资源管理:IronPDF经过优化,可以在与其他库相比占用更少内存的情况下处理大型和复杂的PDF,非常适合高流量应用程序。
  • 异步操作:支持异步 PDF 生成,可以在响应性至关重要的 Web 应用程序中提高性能。

    iTextSharp:

  • 渲染速度: iTextSharp在处理文本密集型PDF时性能良好,但在遇到复杂的布局或图像时可能会显著变慢。
  • 资源管理:在处理大文档或复杂操作时,iTextSharp的内存使用可能会较高,导致在某些情况下出现性能瓶颈。

    PDFsharp:

  • 渲染速度:与IronPDF相比,PDFsharp在处理复杂布局或从HTML转换时通常较慢,因为它缺乏本地HTML渲染引擎。
  • 资源管理:它对内存使用优化较少,并且在处理包含大量图像的大型文件或文档时可能会遇到困难。

    DinkToPdf:

  • 渲染速度:DinkToPdf 使用 wkhtmltopdf 引擎,这对于基本的 HTML 转 PDF 转换是有效的,但在处理更复杂或动态的内容时可能会遇到困难。
  • 资源管理:它通常需要大量的内存和处理能力,并且缺乏对异步操作的原生支持,这限制了它在高负载场景下的性能。

    EvoPDF:

  • 渲染速度:EvoPDF 也提供基于 Chrome 的渲染,类似于 IronPDF,在 HTML 转 PDF 转换中表现良好。
  • 资源管理: 它经过了良好的优化,但在某些情况下可能仍然会比IronPDF消耗更多资源,因为优化力度稍弱。

2. 易用性

IronPDF:

  • API 设计:IronPDF 提供现代直观的 API,易于所有技能水平的开发人员使用。 该库旨在与 .NET 应用程序无缝协作,是 C# 开发人员的最佳选择。
  • 文档和支持:全面的文档、大量代码示例以及出色的客户支持,使得入门和快速解决问题变得简单。
  • 安装和集成:通过NuGet轻松安装,并顺利集成到现有的.NET项目中,所需配置极少。

    iTextSharp:

  • API 设计:iTextSharp 的学习曲线较陡,其 API 更复杂,可能会让初学者感到难以应付。 其灵活性是以简单为代价的。
  • 文档和支持:虽然有详尽的文档,但广泛的配置选项可能使得很难找到用于常见任务的简单示例。
  • 安装和集成: 可通过NuGet获得,但需要更深入了解API才能有效集成。

    PDFsharp:

  • API 设计:PDFsharp 的设计旨在简化基本的 PDF 任务,但缺少开箱即用的高级功能,这可能限制其在更复杂场景中的使用。
  • 文档和支持:基本文档是可用的,但与IronPDF相比,它的范围较小,缺乏高级用法的详细示例。
  • 安装和集成:通过NuGet安装简单,但提供有限的HTML到PDF功能。

    DinkToPdf:

  • API设计:DinkToPdf的API相对简单,但与IronPDF相比不够精致。 它主要针对 HTML 到 PDF 的转换,提供的直接操作 PDF 的功能较少。
  • 文档和支持:文档有限,社区支持不如其他库强大,增加了故障排除的难度。
  • 安装和集成:安装可能更复杂,需要像 wkhtmltopdf 这样的额外依赖项,这会使设置复杂化。

    EvoPDF:

  • API 设计:EvoPDF 提供了一个简洁的 API,类似于 IronPDF,专注于将 HTML 转换为 PDF,同时注重易用性。
  • 文档与支持:文档完善,支持选项良好,但在社区驱动的示例方面不如IronPDF丰富。
  • 安装和集成:可以通过提供的NuGet包轻松集成到.NET项目中。

3. 稳定性

IronPDF:

  • 功能集:IronPDF 功能强大,支持广泛的功能,包括 HTML 转换为 PDF、PDF 编辑、文本提取、加密、注释和数字签名。
  • 错误处理: 提供强大的错误处理和异常管理,使其在生产环境中可靠。
  • 兼容性:完全兼容 .NET Core、.NET 5+ 及旧版 .NET Framework,使其在不同项目类型中具有多功能性。

    iTextSharp:

  • 功能集:iTextSharp 非常强大,具有全面的功能集,几乎支持任何 PDF 任务,包括复杂的操作和表单处理。
  • 错误处理: 良好的错误处理,但由于库的复杂性可能难以管理。
  • 兼容性:非常适合多种环境,包括.NET Framework和.NET Core。

    PDFsharp:

  • 功能集:基本的PDF创建和操作功能。 缺乏一些高级功能,如 HTML 到 PDF 的转换和更复杂的文档编辑。
  • 错误处理:基本错误处理; 与 IronPDF 等更强大的库相比,IronPDF 在复杂场景中的可靠性较低。
  • 兼容性:兼容 .NET Framework 和 .NET Core,但高级功能有限。

    DinkToPdf:

  • 功能集:主要专注于HTML到PDF。 在直接操作 PDF 方面受到限制,缺乏注释和表单处理等高级功能。
  • 错误处理:基本错误处理; 易崩溃或挂起的复杂 HTML 或大文件。
  • 兼容性:与 .NET Core 和 .NET Framework 兼容,但需要外部依赖,可能会引入兼容性问题。

    EvoPDF:

  • 功能集:提供与IronPDF相似的强大功能集,包括高级HTML到PDF转换和一些文档操作功能。
  • 错误处理:在生产环境中实现强大的错误处理和可靠的性能。
  • 兼容性:完全兼容 .NET Core、.NET Framework 和更新的 .NET 版本,使其多功能且可靠。

摘要

  • 性能: IronPDF 和 EvoPDF 由于其基于 Chrome 的渲染引擎在性能上处于领先地位,而 iTextSharp 和 PDFsharp 在处理复杂文档时可能会落后。
  • 易用性:IronPDF以其直观的API和丰富的文档而著称,使其对各个级别的开发者都具有可访问性。 iTextSharp 以简单为代价提供了强大的功能,而 DinkToPdf 和 PDFsharp 则更简单但功能不那么丰富。
  • 稳健性:IronPDF 和 iTextSharp 提供最强大的功能集,其中 IronPDF 提供更简单的集成和诸如异步支持等现代功能,而 iTextSharp 则涵盖了更多利基用例,但学习曲线更陡峭。

全面支持异步编程

IronPDF 无缝集成了异步编程模型,补充了诸如 SemaphoreSlim 之类的并发控制机制。 这样,开发人员就能以最小的工作量构建响应迅速、性能友好的应用程序。

IronPdf 还提供广泛的文档和支持资源,帮助开发人员了解并实施有效的错误处理实践。 这种全面的支持对于在 .NET 项目中排除故障和优化 PDF 操作非常有价值。

IronPDF 提供:

  • 全面文档:广泛且用户友好的文档涵盖所有功能。
  • 24/5 支持:提供活跃的工程师支持。
  • 视频教程:YouTube上提供了逐步视频指南。
  • 社区论坛:活跃社区以提供额外支持。
  • PDF API参考:提供API参考,以便您充分利用我们工具的功能。

    欲了解更多信息,请查看IronPDF的详细文档

结论

在.NET应用程序中使用SemaphoreSlim进行并发管理至关重要,尤其是在处理PDF处理等资源密集型任务时。 通过将SemaphoreSlim与IronPdf集成,开发人员可以实现安全、高效、可靠的并发控制,确保其应用程序保持响应速度快、性能友好。

了解 IronPDF 如何简化您的 PDF 处理工作流程。 试用一下免费试用版,如果您想在项目中继续使用这个强大的工具,价格只需$749起。

C# Semaphoreslim(它如何为开发人员工作):图 8

Chipego
软件工程师
Chipego 拥有出色的倾听技巧,这帮助他理解客户问题并提供智能解决方案。他在 2023 年加入 Iron Software 团队,此前他获得了信息技术学士学位。IronPDF 和 IronOCR 是 Chipego 主要专注的两个产品,但他对所有产品的了解每天都在增长,因为他不断找到支持客户的新方法。他喜欢 Iron Software 的合作氛围,公司各地的团队成员贡献他们丰富的经验,以提供有效的创新解决方案。当 Chipego 离开办公桌时,你经常可以发现他在看书或踢足球。
< 前一页
C# Init 关键字(如何为开发人员工作)
下一步 >
C# try catch finally(开发人员如何使用它)