跳至页脚内容
.NET 帮助

C# Concurrentdictionary(开发者用法)

在使用C#的多线程应用程序时,维护数据完整性至关重要,特别是当你使用像IronPDF这样的库即时生成PDF文档时。 The ConcurrentDictionary<tkey, tvalue> class provides a thread-safe collection to manage key and value pairs efficiently, even when multiple threads concurrently perform operations like insertions, updates, or lookups.

在本指南中,我们将探讨ConcurrentDictionary的工作原理,如何将其与IronPDF整合进行并行PDF处理,以及每个.NET开发者需要了解的关于键类型、线程安全和处理现有键以及确保数据一致性等常见陷阱。

C#中的ConcurrentDictionary是什么?

ConcurrentDictionary<tkey, tvalue>类是System.Collections.Concurrent命名空间的一部分,是一个为高性能、线程安全操作而设计的通用集合。 与常规字典不同,它允许多个线程在不锁定整个结构的情况下安全地访问和修改集合。

一个新的ConcurrentDictionary<string, string>实例可能是这样的:

var dictionary = new ConcurrentDictionary<string, string>();
var dictionary = new ConcurrentDictionary<string, string>();
Dim dictionary = New ConcurrentDictionary(Of String, String)()
$vbLabelText   $csharpLabel

您可以根据您的特定用例定义自己的TKey和TValue类型,例如缓存呈现的PDF文件路径或跟踪并发PDF生成任务。

为什么要与IronPDF一起使用ConcurrentDictionary?

假设您正在构建一个程序,使用IronPDF为数千个用户生成个性化发票。 如果每个线程都需要呈现一个文档并存储其结果,常规字典会引入竞争条件或在键已存在时抛出异常。

使用ConcurrentDictionary可以确保:

  • 数据一致性跨线程
  • 高效的读写
  • 防止未知代码错误
  • 当多个线程在不同的键上操作时没有锁定开销

常用方法及其在IronPDF中的使用

让我们通过IronPDF渲染场景来分解关键方法。

GetOrAdd方法:检索或添加新键

此方法检查指定的键是否存在。 如果不存在,则添加新值。

var filePath = pdfCache.GetOrAdd(userId, id => GeneratePdfForUser(id));
var filePath = pdfCache.GetOrAdd(userId, id => GeneratePdfForUser(id));
Dim filePath = pdfCache.GetOrAdd(userId, Function(id) GeneratePdfForUser(id))
$vbLabelText   $csharpLabel
  • 确保线程安全
  • 避免重复渲染
  • 返回给定键的关联值

AddOrUpdate方法:优雅地处理现有值

此方法允许您在键存在时更新值,或添加新的键值对。

pdfCache.AddOrUpdate(userId,
    id => GeneratePdfForUser(id),
    (id, existingValue) => UpdatePdfForUser(id, existingValue));
pdfCache.AddOrUpdate(userId,
    id => GeneratePdfForUser(id),
    (id, existingValue) => UpdatePdfForUser(id, existingValue));
pdfCache.AddOrUpdate(userId, Function(id) GeneratePdfForUser(id), Function(id, existingValue) UpdatePdfForUser(id, existingValue))
$vbLabelText   $csharpLabel
  • 管理现有键的逻辑
  • 确保访问的成员在并发下是安全的

TryAdd方法:如果键不存在则添加

此方法尝试添加一个值并返回一个指示成功的布尔值。

bool added = pdfCache.TryAdd(userId, pdfBytes);
if (!added)
{
    Console.WriteLine("PDF already cached.");
}
bool added = pdfCache.TryAdd(userId, pdfBytes);
if (!added)
{
    Console.WriteLine("PDF already cached.");
}
Dim added As Boolean = pdfCache.TryAdd(userId, pdfBytes)
If Not added Then
	Console.WriteLine("PDF already cached.")
End If
$vbLabelText   $csharpLabel
  • 完美避免冲突
  • 如果插入成功,方法返回true

用例表:ConcurrentDictionary方法

C# Concurrentdictionary(适用于开发者):图1 - 用例表

优化性能

ConcurrentDictionary支持通过构造函数进行调优:

int concurrencyLevel = 4;
int initialCapacity = 100;
var dictionary = new ConcurrentDictionary<string, byte[]>(concurrencyLevel, initialCapacity);
int concurrencyLevel = 4;
int initialCapacity = 100;
var dictionary = new ConcurrentDictionary<string, byte[]>(concurrencyLevel, initialCapacity);
Dim concurrencyLevel As Integer = 4
Dim initialCapacity As Integer = 100
Dim dictionary = New ConcurrentDictionary(Of String, Byte())(concurrencyLevel, initialCapacity)
$vbLabelText   $csharpLabel
  • concurrencyLevel:预期的线程数(默认=默认并发级别)
  • initialCapacity:预期的元素数量(默认初始容量)

正确设置这些参数可以提高吞吐量并减少多个线程之间的争用。

防止键冲突和默认值问题

当一个键不存在时,TryGetValue之类的操作可能返回类型的默认值:

if (!pdfCache.TryGetValue(userId, out var pdf))
{
    pdf = GeneratePdfForUser(userId); // Second call
}
if (!pdfCache.TryGetValue(userId, out var pdf))
{
    pdf = GeneratePdfForUser(userId); // Second call
}
Dim pdf As var
If Not pdfCache.TryGetValue(userId, pdf) Then
	pdf = GeneratePdfForUser(userId) ' Second call
End If
$vbLabelText   $csharpLabel

这可以保护您的代码免受未知代码或空引用的影响。 在假定存在之前始终检查特定值。

实用示例:线程安全的IronPDF报告生成器

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using IronPdf;
public class Program
{
    static ConcurrentDictionary<string, byte[]> pdfReports =
        new ConcurrentDictionary<string, byte[]>();
    static void Main(string[] args)
    {
        // Simulated user list with HTML content
        var users = new List<User>
        {
            new User { Id = "user1", HtmlContent = "<h1>Report for User 1</h1>" },
            new User { Id = "user2", HtmlContent = "<h1>Report for User 2</h1>" },
            new User { Id = "user3", HtmlContent = "<h1>Report for User 3</h1>" }
        };
        // Generate PDFs concurrently
        var renderer = new ChromePdfRenderer();
        Parallel.ForEach(users, user =>
        {
            var pdf = pdfReports.GetOrAdd(user.Id, id =>
            {
                var pdfDoc = renderer.RenderHtmlAsPdf(user.HtmlContent);
                return pdfDoc.BinaryData;
            });
            SaveToFile(pdf, $"{user.Id}.pdf");
        });
        Console.WriteLine("PDF generation complete.");
    }
    // Utility method to write PDF binary data to file
    static void SaveToFile(byte[] pdfBytes, string filePath)
    {
        File.WriteAllBytes(filePath, pdfBytes);
        Console.WriteLine($"Saved: {filePath}");
    }
}
// Simple user class with ID and HTML content
public class User
{
    public string Id { get; set; }
    public string HtmlContent { get; set; }
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using IronPdf;
public class Program
{
    static ConcurrentDictionary<string, byte[]> pdfReports =
        new ConcurrentDictionary<string, byte[]>();
    static void Main(string[] args)
    {
        // Simulated user list with HTML content
        var users = new List<User>
        {
            new User { Id = "user1", HtmlContent = "<h1>Report for User 1</h1>" },
            new User { Id = "user2", HtmlContent = "<h1>Report for User 2</h1>" },
            new User { Id = "user3", HtmlContent = "<h1>Report for User 3</h1>" }
        };
        // Generate PDFs concurrently
        var renderer = new ChromePdfRenderer();
        Parallel.ForEach(users, user =>
        {
            var pdf = pdfReports.GetOrAdd(user.Id, id =>
            {
                var pdfDoc = renderer.RenderHtmlAsPdf(user.HtmlContent);
                return pdfDoc.BinaryData;
            });
            SaveToFile(pdf, $"{user.Id}.pdf");
        });
        Console.WriteLine("PDF generation complete.");
    }
    // Utility method to write PDF binary data to file
    static void SaveToFile(byte[] pdfBytes, string filePath)
    {
        File.WriteAllBytes(filePath, pdfBytes);
        Console.WriteLine($"Saved: {filePath}");
    }
}
// Simple user class with ID and HTML content
public class User
{
    public string Id { get; set; }
    public string HtmlContent { get; set; }
}
Imports System
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.IO
Imports System.Threading.Tasks
Imports IronPdf
Public Class Program
	Private Shared pdfReports As New ConcurrentDictionary(Of String, Byte())()
	Shared Sub Main(ByVal args() As String)
		' Simulated user list with HTML content
		Dim users = New List(Of User) From {
			New User With {
				.Id = "user1",
				.HtmlContent = "<h1>Report for User 1</h1>"
			},
			New User With {
				.Id = "user2",
				.HtmlContent = "<h1>Report for User 2</h1>"
			},
			New User With {
				.Id = "user3",
				.HtmlContent = "<h1>Report for User 3</h1>"
			}
		}
		' Generate PDFs concurrently
		Dim renderer = New ChromePdfRenderer()
		Parallel.ForEach(users, Sub(user)
			Dim pdf = pdfReports.GetOrAdd(user.Id, Function(id)
				Dim pdfDoc = renderer.RenderHtmlAsPdf(user.HtmlContent)
				Return pdfDoc.BinaryData
			End Function)
			SaveToFile(pdf, $"{user.Id}.pdf")
		End Sub)
		Console.WriteLine("PDF generation complete.")
	End Sub
	' Utility method to write PDF binary data to file
	Private Shared Sub SaveToFile(ByVal pdfBytes() As Byte, ByVal filePath As String)
		File.WriteAllBytes(filePath, pdfBytes)
		Console.WriteLine($"Saved: {filePath}")
	End Sub
End Class
' Simple user class with ID and HTML content
Public Class User
	Public Property Id() As String
	Public Property HtmlContent() As String
End Class
$vbLabelText   $csharpLabel

保存的文件

C# Concurrentdictionary(适用于开发者):图2 - 按指定保存的示例文件

示例输出

C# Concurrentdictionary(适用于开发者):图3 - 示例PDF文档

代码解析

这个示例展示了如何将ConcurrentDictionary<TKey, TValue>与IronPDF结合,生成线程安全的PDF。 这对多个线程同时处理和缓存PDF文件的应用程序来说非常完美。

为什么选择ConcurrentDictionary?

  • 确保对键值对的线程安全访问。
  • GetOrAdd()避免重复生成PDF。
  • 无需手动锁定——非常适合高并发。 如何操作

  • 用户列表中的每个用户都有一个ID和HTML。
  • Parallel.ForEach生成线程以生成PDF。
  • 每个线程使用GetOrAdd()来获取或创建PDF。
  • PDF使用用户的ID作为文件名保存。 总结

此模式在以下情况下非常理想:

  • 您同时为许多用户生成PDF。
  • 您需要性能线程安全。
  • 您希望在C#中实现干净、可靠的并发。

扩展方法和访问模式

虽然ConcurrentDictionary不提供所有LINQ功能,但您仍然可以使用扩展方法查询值:

var completedKeys = pdfReports.Keys.Where(k => k.StartsWith("done-")).ToList();
var completedKeys = pdfReports.Keys.Where(k => k.StartsWith("done-")).ToList();
Dim completedKeys = pdfReports.Keys.Where(Function(k) k.StartsWith("done-")).ToList()
$vbLabelText   $csharpLabel

然而,避免依赖迭代期间复制的元素,因为字典可能会改变。 如果需要,请使用.ToList()或.ToArray()处理快照。

结论:线程安全遇上PDF自动化

ConcurrentDictionary<TKey, TValue>是用于多线程读取/写入键值对的理想选择,使其与IronPDF在多线程应用程序中成为完美搭档。

无论您是缓存已渲染的PDF、跟踪工作状态,还是防止冗余操作,使用这个线程安全的集合确保您的逻辑在性能和可靠性上实现扩展。

立即尝试IronPDF

准备好构建具有完整线程安全性的高性能PDF应用程序了吗? 下载免费试用版的IronPDF,体验无缝的PDF生成结合C#的ConcurrentDictionary强大功能。

常见问题解答

ConcurrentDictionary 如何提升多线程 C# 应用程序的性能?

ConcurrentDictionary 通过允许多个线程同时执行插入、更新和查找等操作而不需要外部锁来提升多线程 C# 应用程序的性能,从而保持数据完整性。

ConcurrentDictionary 与 IronPDF 一起使用的重要性是什么?

使用 ConcurrentDictionary 与 IronPDF 意义重大,因为它允许在并行 PDF 处理中对数据进行线程安全管理,确保动态生成 PDF 文档时多线程环境下高效且无数据冲突。

ConcurrentDictionary 能用于管理 C# 中的并发 PDF 生成吗?

是的,ConcurrentDictionary 可以通过确保操作在多个线程间安全管理,来用于管理 C# 中的并发 PDF 生成,提高 PDF 生成过程的效率和可靠性。

在 C# 中生成 PDF 时为什么线程安全很重要?

在 C# 中生成 PDF 时线程安全很重要,以防止数据损坏和确保一致的输出,特别是在多个线程参与动态创建和修改 PDF 文档时。

使用 ConcurrentDictionary 可以执行哪些并发操作?

使用 ConcurrentDictionary,可以同时执行插入、更新、查找和删除等操作,使其适用于需要线程安全数据管理的高性能应用。

IronPDF 如何处理并发操作?

IronPDF 通过利用像 ConcurrentDictionary 这样的线程安全集合处理并发操作,使得 PDF 处理和数据管理可以在多个线程间高效进行而无数据完整性风险。

使用 ConcurrentDictionary 时是否必须实现外部锁定?

不,使用 ConcurrentDictionary 时不需要实现外部锁定,因为其设计本身就是线程安全的,能够内部管理并发操作。

开发者如何优化 C# 应用程序中的 PDF 处理?

开发者可以通过将像 ConcurrentDictionary 这样的线程安全集合与 IronPDF 等库集成,来优化 C# 应用程序中的 PDF 处理,从而实现高效且可靠的并行 PDF 文档处理。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。