Przejdź do treści stopki
POMOC .NET

Lista współbieżna w języku C# (jak działa dla programistów)

Jeśli kiedykolwiek miałeś wiele wątków walczących o dostęp do współdzielonego zasobu, wiesz, że implementacja bezpieczna dla wątków to nie jest gra. Ale nie martw się! C# ma dla ciebie rozwiązanie w postaci kolekcji współbieżnych - potężnego zestawu klas kolekcji bezpiecznych dla wątków, które zapewniają bezpieczeństwo wątkowe w pełnym stylu i gracji.

Bezpieczeństwo wątków i kolekcje współbieżne w C

Zacznijmy od wyobrażenia sobie ruchliwego skrzyżowania w mieście bez sygnalizacji świetlnej. Możesz sobie wyobrazić chaos! To jest podobne do tego, co się dzieje, gdy wiele wątków równocześnie ma dostęp do współdzielonego zasobu bez odpowiedniego systemu. Na szczęście w C# mamy sygnalizację świetlną dla naszych wątków - nazywają się one kolekcjami współbieżnymi. Są to klasy kolekcji, które umożliwiają dostęp do zasobu tylko jednemu wątkowi naraz. Ta bezpieczeństwo wątkowe jest kluczowe podczas pracy z wieloma wątkami.

Odkrywanie współbieżnych kolekcji bezpiecznych dla wątków w C

W C# przestrzeń nazw System.Collections.Concurrent posiada różnorodne klasy kolekcji współbieżnych, takie jak ConcurrentDictionary, ConcurrentQueue, ConcurrentStack i ConcurrentBag. Te nieuporządkowane klasy kolekcji oferują bezpieczną dla wątków wersję swoich niekonkurencyjnych odpowiedników. Co wyróżnia kolekcje współbieżne, to fakt, że są one nieuporządkowanymi kolekcjami współbieżnymi, co oznacza, że elementy nie mają określonego porządku. Na przykład, z listą współbieżną, nie wiesz dokładnie, gdzie element zostaje wstawiony. Skupieniem jest zapewnienie bezpieczeństwa wątkowego, a nie utrzymanie porządku.

Weźmy rzeczywisty przykład. Pomyśl o przesyłaniu hasła na stronie internetowej. Dzięki kolekcji współbieżnej wielu użytkowników może jednocześnie przesyłać swoje hasła. Każda akcja 'prześlij' jest jak wątek, a kolekcja współbieżna zapewnia, że każda przesyłka jest bezpieczna dla wątków, przetwarzana bezpiecznie i skutecznie.

ConcurrentDictionary: Przykład z życia wzięty

Teraz zbadajmy klasę kolekcji ConcurrentDictionary na przykładzie z życia wziętym. Wyobraź sobie internetową księgarnię z funkcją rekomendacji. Każdy klik użytkownika dodaje książkę do jego osobistej listy rekomendacji, przedstawionej jako słownik. Kiedy wielu użytkowników przegląda i klika książki jednocześnie, mamy wiele wątków równocześnie uzyskujących dostęp do słownika.

Przykład ConcurrentDictionary w C# wyglądałby tak:

using System.Collections.Concurrent;

ConcurrentDictionary<string, string> recommendedBooks = new ConcurrentDictionary<string, string>();
using System.Collections.Concurrent;

ConcurrentDictionary<string, string> recommendedBooks = new ConcurrentDictionary<string, string>();
Imports System.Collections.Concurrent

Private recommendedBooks As New ConcurrentDictionary(Of String, String)()
$vbLabelText   $csharpLabel

Aby dodać książkę do całej kolekcji rekomendacji użytkownika, moglibyśmy użyć metody TryAdd:

public void Insert(string user, string book)
{
    // Try to add the book to the user's recommendations
    recommendedBooks.TryAdd(user, book);
}
public void Insert(string user, string book)
{
    // Try to add the book to the user's recommendations
    recommendedBooks.TryAdd(user, book);
}
Public Sub Insert(ByVal user As String, ByVal book As String)
	' Try to add the book to the user's recommendations
	recommendedBooks.TryAdd(user, book)
End Sub
$vbLabelText   $csharpLabel

W tym scenariuszu klasa kolekcji ConcurrentDictionary zapewnia, że każde kliknięcie (czyli 'wątek') jest obsługiwane indywidualnie, więc rekomendacje dwóch różnych użytkowników nie ulegną pomieszaniu. Zarządza ona całym bezpieczeństwem wątkowym, więc nie musisz się martwić o wyścigi danych i inne problemy z równoczesnym dostępem związane z wieloma wątkami.

Implementacja operacji bezpiecznych dla wątków

Oprócz TryAdd, kolekcje współbieżne w C# oferują różnorodne inne operacje bezpieczne dla wątków takie jak TryRemove i TryUpdate. Te metody zapewniają, że tylko jeden wątek może wykonywać operację w danym czasie. Więc na przykład, jeśli chcielibyśmy usunąć książkę z rekomendacji użytkownika w poprzednim przykładzie, moglibyśmy użyć metody TryRemove:

public void RemoveAt(string user)
{
    // Attempt to remove the book for the specified user
    string removedBook;
    recommendedBooks.TryRemove(user, out removedBook);
}
public void RemoveAt(string user)
{
    // Attempt to remove the book for the specified user
    string removedBook;
    recommendedBooks.TryRemove(user, out removedBook);
}
Public Sub RemoveAt(ByVal user As String)
	' Attempt to remove the book for the specified user
	Dim removedBook As String = Nothing
	recommendedBooks.TryRemove(user, removedBook)
End Sub
$vbLabelText   $csharpLabel

Metoda TryRemove próbuje usunąć wartość podanego klucza (w tym przypadku użytkownika) i umieścić ją w zmiennej removedBook.

Kopiowanie kolekcji współbieżnych

Teraz załóżmy, że chcesz skopiować swoją kolekcję współbieżną do tablicy. Kolekcje współbieżne oferują metodę CopyTo dokładnie w tym celu:

public void CopyTo()
{
    // Create an array to hold the recommended books
    string[] bookArray = new string[recommendedBooks.Count];

    // Copy the values of the concurrent dictionary to the array
    recommendedBooks.Values.CopyTo(bookArray, 0);
}
public void CopyTo()
{
    // Create an array to hold the recommended books
    string[] bookArray = new string[recommendedBooks.Count];

    // Copy the values of the concurrent dictionary to the array
    recommendedBooks.Values.CopyTo(bookArray, 0);
}
Public Sub CopyTo()
	' Create an array to hold the recommended books
	Dim bookArray(recommendedBooks.Count - 1) As String

	' Copy the values of the concurrent dictionary to the array
	recommendedBooks.Values.CopyTo(bookArray, 0)
End Sub
$vbLabelText   $csharpLabel

Tutaj metoda CopyTo kopiuje wszystkie książki (wartości) z współbieżnego słownika recommendedBooks do bookArray.

Kolekcje bezpieczne dla wątków

C# oferuje również kolekcje bezpieczne dla wątków, które są zaprojektowane w celu zapewnienia bezpiecznego dostępu do współdzielonych zasobów w środowiskach wielowątkowych. Te kolekcje, takie jak ConcurrentBag, ConcurrentQueue i ConcurrentStack, oferują bezpieczne dla wątków implementacje, gdzie wiele wątków może uzyskiwać dostęp i modyfikować kolekcję współbieżnie bez powodowania konfliktów lub uszkodzenia danych.

Gwarantują one spójność i integralność, obsługując synchronizację wewnętrznie, co czyni je idealnymi dla scenariuszy, gdzie wystarcza nieuporządkowana kolekcja, a bezpieczeństwo wątkowe jest niezwykle ważne w aplikacjach C#.

Dowiedz się więcej o IronPDF to popularna biblioteka C# umożliwiająca generowanie dokumentów PDF z HTML w łatwy sposób.

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

Chociaż może się wydawać, że nie jest bezpośrednio związane z listami współbieżnymi, IronPDF może uzupełniać operacje na kolekcjach współbieżnych, zapewniając łatwy sposób tworzenia raportów PDF, logów lub innych dokumentów, które uchwycą wyniki twojego przetwarzania współbieżnego.

Weź pod uwagę scenariusz, w którym masz aplikację wielowątkową wykonującą intensywne przetwarzanie danych. Gdy wątki działają na danych, możesz chcieć uchwycić wyniki i wygenerować raport PDF do dalszej analizy lub dokumentacji. Właśnie tutaj IronPDF wchodzi w grę.

Korzystanie z IronPDF jest tak proste, jak dodanie biblioteki do swojego projektu i skorzystanie z jego wygodnego API. Oto przykład, jak można zintegrować IronPDF z operacjami na kolekcjach współbieżnych:

using IronPdf;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

// Create a concurrent dictionary to hold your processed data
ConcurrentDictionary<int, string> processedData = new ConcurrentDictionary<int, string>();

// Define your data list (replace with your actual data source)
List<DataItem> dataList = GetDataList();

// Process your data concurrently and store the results in the dictionary
Parallel.ForEach(dataList, (dataItem) =>
{
    // Process each data item and add the result to the dictionary
    string processedResult = ProcessDataItem(dataItem);
    processedData.TryAdd(dataItem.Id, processedResult);
});

// Generate a PDF report with the processed data
var renderer = new ChromePdfRenderer();
var pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData));
pdfDocument.SaveAs("C:\\processed_data_report.pdf");

// Method to retrieve the data list (replace with your actual data source logic)
List<DataItem> GetDataList()
{
    List<DataItem> dataList = new List<DataItem>()
    {
        new DataItem { Id = 1, Name = "Item 1" },
        new DataItem { Id = 2, Name = "Item 2" },
        new DataItem { Id = 3, Name = "Item 3" },
        new DataItem { Id = 4, Name = "Item 4" }
    };
    return dataList;
}

// Method to process each data item and return the result (replace with your actual data processing logic)
string ProcessDataItem(DataItem dataItem)
{
    // Simulating data processing with a delay
    Task.Delay(100).Wait();
    return $"Processed: {dataItem.Name}";
}

// Method to build the HTML report using the processed data (replace with your actual reporting logic)
string BuildHtmlReport(ConcurrentDictionary<int, string> processedData)
{
    string html = "<h1>Processed Data Report</h1><ul>";
    foreach (var kvp in processedData)
    {
        html += $"<li>Item {kvp.Key}: {kvp.Value}</li>";
    }
    html += "</ul>";
    return html;
}

// Placeholder class for your data item (replace with your actual data item class)
public class DataItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    // Add other properties as needed
}
using IronPdf;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

// Create a concurrent dictionary to hold your processed data
ConcurrentDictionary<int, string> processedData = new ConcurrentDictionary<int, string>();

// Define your data list (replace with your actual data source)
List<DataItem> dataList = GetDataList();

// Process your data concurrently and store the results in the dictionary
Parallel.ForEach(dataList, (dataItem) =>
{
    // Process each data item and add the result to the dictionary
    string processedResult = ProcessDataItem(dataItem);
    processedData.TryAdd(dataItem.Id, processedResult);
});

// Generate a PDF report with the processed data
var renderer = new ChromePdfRenderer();
var pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData));
pdfDocument.SaveAs("C:\\processed_data_report.pdf");

// Method to retrieve the data list (replace with your actual data source logic)
List<DataItem> GetDataList()
{
    List<DataItem> dataList = new List<DataItem>()
    {
        new DataItem { Id = 1, Name = "Item 1" },
        new DataItem { Id = 2, Name = "Item 2" },
        new DataItem { Id = 3, Name = "Item 3" },
        new DataItem { Id = 4, Name = "Item 4" }
    };
    return dataList;
}

// Method to process each data item and return the result (replace with your actual data processing logic)
string ProcessDataItem(DataItem dataItem)
{
    // Simulating data processing with a delay
    Task.Delay(100).Wait();
    return $"Processed: {dataItem.Name}";
}

// Method to build the HTML report using the processed data (replace with your actual reporting logic)
string BuildHtmlReport(ConcurrentDictionary<int, string> processedData)
{
    string html = "<h1>Processed Data Report</h1><ul>";
    foreach (var kvp in processedData)
    {
        html += $"<li>Item {kvp.Key}: {kvp.Value}</li>";
    }
    html += "</ul>";
    return html;
}

// Placeholder class for your data item (replace with your actual data item class)
public class DataItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    // Add other properties as needed
}
Imports IronPdf
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks

' Create a concurrent dictionary to hold your processed data
Private processedData As New ConcurrentDictionary(Of Integer, String)()

' Define your data list (replace with your actual data source)
Private dataList As List(Of DataItem) = GetDataList()

' Process your data concurrently and store the results in the dictionary
Parallel.ForEach(dataList, Sub(dataItem)
	' Process each data item and add the result to the dictionary
	Dim processedResult As String = ProcessDataItem(dataItem)
	processedData.TryAdd(dataItem.Id, processedResult)
End Sub)

' Generate a PDF report with the processed data
Dim renderer = New ChromePdfRenderer()
Dim pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData))
pdfDocument.SaveAs("C:\processed_data_report.pdf")

' Method to retrieve the data list (replace with your actual data source logic)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'List(Of DataItem) GetDataList()
'{
'	List<DataItem> dataList = New List<DataItem>() { New DataItem { Id = 1, Name = "Item 1" }, New DataItem { Id = 2, Name = "Item 2" }, New DataItem { Id = 3, Name = "Item 3" }, New DataItem { Id = 4, Name = "Item 4" } };
'	Return dataList;
'}

' Method to process each data item and return the result (replace with your actual data processing logic)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'string ProcessDataItem(DataItem dataItem)
'{
'	' Simulating data processing with a delay
'	Task.Delay(100).Wait();
'	Return string.Format("Processed: {0}", dataItem.Name);
'}

' Method to build the HTML report using the processed data (replace with your actual reporting logic)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'string BuildHtmlReport(ConcurrentDictionary(Of int, string) processedData)
'{
'	string html = "<h1>Processed Data Report</h1><ul>";
'	foreach (var kvp in processedData)
'	{
'		html += string.Format("<li>Item {0}: {1}</li>", kvp.Key, kvp.Value);
'	}
'	html += "</ul>";
'	Return html;
'}

' Placeholder class for your data item (replace with your actual data item class)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'public class DataItem
'{
'	public int Id
'	{
'		get;
'		set;
'	}
'	public string Name
'	{
'		get;
'		set;
'	}
'	' Add other properties as needed
'}
$vbLabelText   $csharpLabel

Oto wynik kodu:

C# Listy Współbieżne (Jak Działa Dla Programistów) Rysunek 1 - Wynik

Podsumowanie

Podsumowując, zrozumienie i wykorzystanie współbieżnych kolekcji C#, takich jak listy współbieżne, może znacznie poprawić twoją zdolność do obsługi scenariuszy wielowątkowych i zapewniania bezpieczeństwa wątkowego w twoich aplikacjach. Dzięki kolekcjom współbieżnym możesz zarządzać współdzielonymi zasobami efektywnie, zapobiegając wyścigom danych i kolizjom między wątkami.

Integracja zewnętrznych bibliotek jak IronPDF może dodatkowo zwiększyć funkcjonalność kolekcji współbieżnych, umożliwiając generowanie atrakcyjnych wizualnie raportów PDF lub dokumentów. IronPDF oferuje bezpłatną wersję próbną swojej biblioteki do konwersji HTML na PDF, umożliwiając ci eksplorację jej możliwości oraz opcje licencyjne zaczynające się od $799.

Często Zadawane Pytania

Czym są współbieżne kolekcje w C#?

Współbieżne kolekcje w C# to zbiór bezpiecznych dla wątków, generycznych klas kolekcji, które zapewniają bezpieczeństwo wątków, gdy wiele wątków uzyskuje dostęp do współdzielonych zasobów.

Dlaczego bezpieczeństwo wątków jest ważne w C#?

Bezpieczeństwo wątków jest kluczowe w C# w celu zapobiegania chaosowi i uszkodzeniu danych, gdy wiele wątków jednocześnie uzyskuje dostęp do i modyfikuje współdzielone zasoby. Zapewnia, że operacje są wykonywane w kontrolowany sposób.

Jak mogę stworzyć listę bezpieczną dla wątków w C#?

Chociaż C# nie zapewnia bezpośrednio klasy List bezpiecznej dla wątków, można użyć innych współbieżnych kolekcji, takich jak `ConcurrentBag` lub `ConcurrentDictionary` do podobnych operacji bezpiecznych dla wątków.

Czym jest ConcurrentDictionary w C#?

`ConcurrentDictionary` w C# to klasa kolekcji bezpieczna dla wątków w przestrzeni nazw `System.Collections.Concurrent`. Pozwala wielu wątkom bezpiecznie dodawać, aktualizować i usuwać pary klucz-wartość jednocześnie.

Jak ConcurrentDictionary zapewnia bezpieczeństwo wątków?

`ConcurrentDictionary` zapewnia bezpieczeństwo wątków, obsługując synchronizację wewnętrznie, pozwalając tylko jednemu wątkowi na wykonywanie operacji, takich jak dodawanie czy usuwanie elementów naraz.

Jak można dodać element do ConcurrentDictionary?

Możesz dodać element do `ConcurrentDictionary` używając metody TryAdd, która próbuje dodać parę klucz-wartość tylko wtedy, gdy klucz jeszcze nie istnieje w słowniku.

Jaki jest cel metody CopyTo w współbieżnych kolekcjach?

Metoda `CopyTo` w współbieżnych kolekcjach jest używana do kopiowania elementów kolekcji do tablicy, co pozwala na przeniesienie danych z kolekcji do innego formatu przechowywania.

Czy IronPDF może być używany do generowania raportów PDF z przetworzonych danych?

Tak, IronPDF może być używany do generowania raportów PDF z danych przetworzonych przez aplikacje wielowątkowe, rejestrując wyniki współbieżnych operacji.

Jak użycie IronPDF zwiększa funkcjonalność współbieżnych operacji?

IronPDF zwiększa operacje współbieżne, umożliwiając tworzenie dokumentów PDF z przetworzonych danych, oferując sposób na dokumentowanie i udostępnianie wyników przetwarzania wielowątkowego.

Jaką rolę pełni IronPDF w aplikacjach wielowątkowych C#?

IronPDF pozwala deweloperom generować raporty PDF z danych przetworzonych równolegle, ułatwiając konsolidację i udostępnianie wyników operacji wielowątkowych.

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