Przejdź do treści stopki
POMOC .NET

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)()
$vbLabelText   $csharpLabel

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))
$vbLabelText   $csharpLabel
  • 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))
$vbLabelText   $csharpLabel
  • 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
$vbLabelText   $csharpLabel
  • Idealna do unikania konfliktów
  • Metoda zwraca wartość true, jeśli wstawienie się powiedzie

Tabela przypadków użycia: Metody ConcurrentDictionary

C# Concurrentdictionary (Jak działa dla deweloperów): Rysunek 1 - Tabela przypadków użycia

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)
$vbLabelText   $csharpLabel
  • 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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

Zapisane pliki

C# Concurrentdictionary (Jak działa dla deweloperów): Rysunek 2 - Przykładowe pliki zapisane według specyfikacji

Przykładowy wynik

C# Concurrentdictionary (Jak działa dla deweloperów): Rysunek 3 - Przykładowy dokument PDF

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()
$vbLabelText   $csharpLabel

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.

Jacob Mellor, Dyrektor Technologiczny @ Team Iron
Dyrektor ds. technologii

Jacob Mellor jest Chief Technology Officer w Iron Software i wizjonerskim inżynierem, pionierem technologii C# PDF. Jako pierwotny deweloper głównej bazy kodowej Iron Software, kształtuje architekturę produktów firmy od jej początku, przekształcając ją wspólnie z CEO Cameron Rimington w firmę liczą...

Czytaj więcej

Zespol wsparcia Iron

Jestesmy online 24 godziny, 5 dni w tygodniu.
Czat
Email
Zadzwon do mnie