C# Concurrentdictionary (Jak to działa dla deweloperów)
Podczas pracy z aplikacjami wielowątkowymi w C#, utrzymanie integralności danych jest kluczowe, zwłaszcza kiedy generujesz dokumenty PDF na bieżąco, używając biblioteki takiej jak IronPDF. Klasa ConcurrentDictionary<tkey, tvalue> zapewnia typową dla wątków kolekcję do efektywnego zarządzania parami klucz-wartość, nawet gdy wiele wątków jednocześnie wykonuje operacje takie jak wstawianie, aktualizacja lub wyszukiwanie.
W tym przewodniku przeanalizujemy, jak działa ConcurrentDictionary, jak można go zintegrować z IronPDF do równoległego przetwarzania PDF, oraz co każdy programista .NET powinien wiedzieć o typach kluczy, bezpieczeństwie wątków i częstych pułapkach, takich jak obsługa istniejącego klucza lub zapewnianie spójności danych.
Czym jest ConcurrentDictionary w C#?
Klasa ConcurrentDictionary<tkey, tvalue>, będąca częścią przestrzeni nazw System.Collections.Concurrent, to ogólna kolekcja zaprojektowana do operacji o wysokiej wydajności i wątkoodporności. W przeciwieństwie do zwykłego słownika, pozwala na bezpieczny dostęp i modyfikację kolekcji przez wiele wątków bez blokowania całej struktury.
Nowa instancja ConcurrentDictionary<string, string> może wyglądać tak:
var dictionary = new ConcurrentDictionary<string, string>();
var dictionary = new ConcurrentDictionary<string, string>();
Dim dictionary = New ConcurrentDictionary(Of String, String)()
Możesz zdefiniować własne typy TKey i TValue w oparciu o konkretne zastosowanie, takie jak buforowanie ścieżek do wyrenderowanych plików PDF lub śledzenie równoległych zadań generowania PDF.
Dlaczego warto używać ConcurrentDictionary z IronPDF?
Wyobraź sobie, że budujesz program, który generuje personalizowane faktury za pomocą IronPDF dla tysięcy użytkowników. Jeśli każdy wątek musi wyrenderować dokument i przechowywać wynik, zwykły słownik wprowadziłby warunki wyścigu lub generował wyjątki, jeśli klucz już istnieje.
Używanie ConcurrentDictionary zapewnia:
- Spójność danych w wątkach
- Efektywne operacje odczytu i zapisu
- Zapobieganie nieznanym błędom w kodzie
- Brak narzutów blokowania, gdy wiele wątków działa na różnych kluczach
Popularne metody i ich użycie z IronPDF
Przeanalizujmy kluczowe metody przy użyciu scenariuszy renderowania IronPDF.
Metoda GetOrAdd: Pobierz lub dodaj nowy klucz
Ta metoda sprawdza, czy istnieje określony klucz. Jeśli nie, dodaje nową wartość.
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))
- Zapewnia bezpieczeństwo wątków
- Unika podwójnego renderowania
- Zwraca powiązaną wartość dla danego klucza
Metoda AddOrUpdate: Zarządzanie istniejącą wartością w sposób przyjazny
Ta metoda pozwala na aktualizację wartości, jeśli klucz istnieje, lub dodanie nowej pary klucz-wartość.
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))
- Zarządza logiką dla istniejącego klucza
- Zapewnia, że członkowie są dostępni w warunkach współbieżności
Metoda TryAdd: Dodaj, jeśli klucz nie istnieje
Ta metoda próbuje dodać wartość i zwraca wartość logiczną wskazującą na sukces.
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
- Idealna do unikania konfliktów
- Metoda zwraca wartość true, jeśli wstawienie się powiedzie
Tabela przypadków użycia: Metody ConcurrentDictionary

Optymalizacja wydajności
ConcurrentDictionary wspiera dostrajanie przez konstruktor:
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)
- poziom współbieżności: spodziewana liczba wątków (domyślnie = domyślny poziom współbieżności)
- początkowa pojemność: spodziewana liczba elementów (domyślna początkowa pojemność)
Poprawne ustawienie tych parametrów zwiększa przepustowość i zmniejsza rywalizację w wątkach.
Zapobieganie problemom z konfliktami kluczy i domyślnymi ustawieniami
Gdy klucz nie istnieje, operacje takie jak TryGetValue mogą zwracać wartość domyślną dla typu:
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
To zabezpiecza twój kod przed nieznanym kodem lub odwołaniami null. Zawsze sprawdzaj dla konkretnej wartości, zanim założysz obecność.
Praktyczny przykład: Wątko-odporny generator raportów 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
Zapisane pliki

Przykładowy wynik

Analiza kodu
Ten przykład pokazuje, jak połączyć ConcurrentDictionary<TKey, TValue> z IronPDF, aby generować PDF-y w sposób bezpieczny dla wątku. Jest idealny dla aplikacji, w których wiele wątków jednocześnie przetwarza i buforuje pliki PDF.
Dlaczego ConcurrentDictionary?
- Zapewnia bezpieczny dla wątku dostęp do par klucz-wartość.
- GetOrAdd() unika podwójnego generowania PDF.
-
Nie potrzeba ręcznych blokad—idealne dla wysokiej współbieżności. Jak to działa
- Lista użytkowników, z których każdy posiada ID i HTML.
- Parallel.ForEach tworzy wątki do generowania PDF.
- Każdy wątek używa GetOrAdd(), aby pobrać lub stworzyć PDF.
- PDF jest zapisywany z użyciem ID użytkownika jako nazwy pliku. Podsumowanie
Ten wzór jest idealny, gdy:
- Generujesz PDF-y dla wielu użytkowników jednocześnie.
- Potrzebujesz wydajności i bezpieczeństwa wątków.
- Chcesz czystości i niezawodności współbieżności w C#.
Metody rozszerzeń i wzorce dostępu
Chociaż ConcurrentDictionary nie udostępnia wszystkich funkcji LINQ, możesz nadal używać metod rozszerzeń do zapytań o wartości:
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()
Jednak unikaj polegania na elementach skopiowanych podczas iteracji, ponieważ słownik może się zmienić. Użyj .ToList() lub .ToArray(), aby korzystać ze zrzutu, jeśli to potrzebne.
Podsumowanie: Bezpieczeństwo wątków spotyka automatyzację PDF
ConcurrentDictionary<TKey, TValue> jest idealny dla scenariuszy, w których wiele wątków musi jednocześnie czytać/zapisywać pary klucz-wartość—co czyni go idealnym partnerem dla IronPDF w aplikacjach wielowątkowych.
Niezależnie od tego, czy buforujesz wyrenderowane PDF, śledzisz status zadań, czy zapobiegasz powtarzającym się operacjom, użycie tej kolekcji odpornej na wątki zapewnia skalowanie twojej logiki wraz z wydajnością i niezawodnością.
Wypróbuj IronPDF już dziś
Gotowy do budowy wysokowydajnych aplikacji PDF z pełnym bezpieczeństwem wątków? Pobierz bezpłatną wersję próbną IronPDF i doświadcz płynnego generowania PDF połączonego z mocą ConcurrentDictionary w C#.
Często Zadawane Pytania
Jak ConcurrentDictionary poprawia wydajność w aplikacjach wielowątkowych C#?
ConcurrentDictionary poprawia wydajność w aplikacjach wielowątkowych C#, pozwalając wielu wątkom na wykonywanie operacji takich jak wstawianie, aktualizowanie i wyszukiwanie jednocześnie bez potrzeby zewnętrznych blokad, tym samym utrzymując integralność danych.
Jakie jest znaczenie używania ConcurrentDictionary z IronPDF?
Użycie ConcurrentDictionary z IronPDF jest istotne, ponieważ pozwala na zarządzanie danymi bez ryzyka dla bezpieczeństwa wątków podczas równoległego przetwarzania PDF, zapewniając, że generowanie PDF jest wydajne i wolne od konfliktów danych w środowiskach wielowątkowych.
Czy ConcurrentDictionary można używać do zarządzania równoczesnym generowaniem PDF w C#?
Tak, ConcurrentDictionary można używać do zarządzania równoczesnym generowaniem PDF w C#, zapewniając, że operacje są bezpiecznie obsługiwane na kilku wątkach, poprawiając efektywność i niezawodność procesu generowania PDF.
Dlaczego bezpieczeństwo wątków jest ważne przy generowaniu PDF w C#?
Bezpieczeństwo wątków jest ważne podczas generowania PDF w C#, aby zapobiec uszkodzeniom danych i zapewnić spójną produkcję, zwłaszcza gdy wiele wątków jest zaangażowanych w dynamiczne tworzenie i modyfikację dokumentów PDF.
Jakie operacje można wykonywać równocześnie, używając ConcurrentDictionary?
Operacje takie jak wstawianie, aktualizowanie, wyszukiwanie i usuwanie można wykonywać równocześnie, używając ConcurrentDictionary, co czyni ją idealną dla aplikacji o wysokiej wydajności wymagających bezpiecznego zarządzania danymi dla wątków.
Jak IronPDF obsługuje operacje równoczesne?
IronPDF obsługuje operacje równoczesne, wykorzystując kolekcje bezpieczne dla wątków, takie jak ConcurrentDictionary, co pozwala na efektywne przetwarzanie PDF i zarządzanie danymi na wielu wątkach bez ryzyka utraty integralności danych.
Czy konieczne jest stosowanie zewnętrznych mechanizmów blokady przy użyciu ConcurrentDictionary?
Nie, nie jest konieczne stosowanie zewnętrznych mechanizmów blokady przy użyciu ConcurrentDictionary, ponieważ jest ona zaprojektowana jako z natury bezpieczna dla wątków, zarządzając operacjami równoczesnymi wewnętrznie.
Jak deweloperzy mogą zoptymalizować przetwarzanie PDF w aplikacjach C#?
Deweloperzy mogą zoptymalizować przetwarzanie PDF w aplikacjach C# poprzez integrację kolekcji bezpiecznych dla wątków, takich jak ConcurrentDictionary, z bibliotekami takimi jak IronPDF, umożliwiając efektywną i niezawodną równoległą obróbkę dokumentów PDF.




