
Lista concurrente en C# (Cómo funciona para desarrolladores)
Si alguna vez has tenido múltiples hilos compitiendo por el acceso a un recurso compartido, sabes que la implementación a prueba de hilos no es un juego. ¡Pero no te preocupes! C# te tiene cubierto con colecciones concurrentes, un poderoso conjunto de clases genéricas de colección a prueba de hilos que aseguran la seguridad de los hilos con estilo y elegancia.
Thread Safety and Concurrent Collections in C#
Comencemos imaginando una intersección de una ciudad bulliciosa sin semáforos. ¡Puedes imaginar el caos! Aprenda a usar colecciones concurrentes en C# para garantizar la seguridad de hilos cuando múltiples hilos acceden a recursos compartidos en escenarios de multithreading. Afortunadamente, en C#, tenemos semáforos para nuestros hilos: estos se llaman colecciones concurrentes. Son clases de colección que permiten que sólo un hilo acceda a un recurso a la vez. Esta seguridad de hilos es crucial al trabajar con múltiples hilos.
Exploring Concurrent Thread Safe Collections in C#
En C#, el espacio de nombres System.Collections.Concurrent tiene una variedad de clases de colecciones concurrentes, como ConcurrentDictionary, ConcurrentQueue, ConcurrentStack, y ConcurrentBag. Estas clases de colección desordenadas proporcionan una versión a prueba de hilos de sus contrapartes no concurrentes. Lo que diferencia a las colecciones concurrentes es que son colecciones concurrentes desordenadas, lo que significa que los elementos no tienen un orden específico. Por ejemplo, con una lista concurrente, no sabes exactamente dónde se inserta un elemento. El enfoque está en asegurar la seguridad de los hilos, no en mantener un orden.
Tomemos un ejemplo de la vida real. Piensa en una publicación de envío de contraseñas en un sitio web. Con una colección concurrente, múltiples usuarios pueden enviar sus contraseñas simultáneamente. Cada acción de 'enviar' es como un hilo, y la colección concurrente asegura que cada envío sea seguro para los hilos, procesado de forma segura y efectiva.
ConcurrentDictionary: Un ejemplo del mundo real
Ahora, exploremos la clase de colección ConcurrentDictionary con un ejemplo de la vida real. Imagina una librería en línea con una función de recomendaciones. Cada clic de un usuario agrega un libro a su lista personal de recomendaciones, representada por un diccionario. A medida que múltiples usuarios navegan y hacen clic en libros al mismo tiempo, tenemos múltiples hilos accediendo al diccionario de manera concurrente.
Un ConcurrentDictionary en C# se vería algo así:
using System.Collections.Concurrent;
ConcurrentDictionary<string, string> recommendedBooks = new ConcurrentDictionary<string, string>();Imports System.Collections.Concurrent
Private recommendedBooks As New ConcurrentDictionary(Of String, String)()Para agregar un libro a toda la colección de recomendaciones de un usuario, podríamos usar el método TryAdd:
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 SubEn este escenario, la clase de colección ConcurrentDictionary asegura que cada clic (o 'hilo') se maneje de manera individual, así que no se mezclan las recomendaciones de dos usuarios. Maneja toda la seguridad de hilos, por lo que no tienes que preocuparte por las carreras de datos y otros problemas de concurrencia relacionados con múltiples hilos.
Implementación de operaciones seguras de subprocesos
Además de TryAdd, las colecciones concurrentes en C# proporcionan una variedad de otras operaciones seguras para hilos como TryRemove y TryUpdate. Estos métodos aseguran que solo un hilo pueda realizar una operación a la vez. Así que, por ejemplo, si quisiéramos eliminar un libro de las recomendaciones de un usuario en el ejemplo anterior, podríamos usar el método TryRemove:
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 SubEl método TryRemove intentará eliminar el valor de la clave proporcionada (en este caso, un usuario) y ponerlo en la variable removedBook.
Copiado de colecciones concurrentes
Ahora, digamos que quieres copiar tu colección concurrente a un arreglo. Las colecciones concurrentes proporcionan un método CopyTo para este propósito exacto:
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 As String() = New String(recommendedBooks.Count - 1) {}
' Copy the values of the concurrent dictionary to the array
recommendedBooks.Values.CopyTo(bookArray, 0)
End SubAquí, el método CopyTo copia todos los libros (valores) del diccionario concurrente recommendedBooks en el bookArray.
Recogida segura de hilos
C# también proporciona colecciones seguras para hilos, que están diseñadas para asegurar un acceso seguro a recursos compartidos en entornos multihilo. Estas colecciones, como ConcurrentBag, ConcurrentQueue, y ConcurrentStack, ofrecen implementaciones seguras para hilos donde múltiples hilos pueden acceder y modificar la colección concurrentemente sin causar conflictos o corrupción de datos.
Garantizan consistencia e integridad manejando la sincronización internamente, haciéndolas ideales para escenarios donde una colección desordenada es suficiente, y la seguridad de hilos es de suma importancia en tus aplicaciones C#.
Aprende Más Sobre IronPDF es una popular biblioteca de C# que te permite generar documentos PDF a partir de HTML sin esfuerzo.
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 ClassAunque al principio puede no parecer directamente relacionado con las listas concurrentes, IronPDF puede complementar tus operaciones de colección concurrente proporcionando una manera fácil de crear informes en PDF, registros, o cualquier otro documento que capture los resultados de tu procesamiento concurrente.
Considera el escenario donde tienes una aplicación multihilo que realiza un procesamiento de datos intensivo. A medida que los hilos trabajan su magia en los datos, podrías querer capturar los resultados y generar un informe en PDF para un análisis más profundo o para llevar un registro. Aquí es donde entra en juego IronPDF.
Usar IronPDF es tan simple como agregar la biblioteca a tu proyecto y utilizar su cómodo API. Aquí tienes un ejemplo de cómo puedes integrar IronPDF con tus operaciones de colección concurriente:
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
'}Aquí está la salida del código:

Conclusión
En conclusión, entender y utilizar colecciones concurrentes de C#, como listas concurrentes, puede mejorar enormemente tu habilidad para manejar escenarios multihilo y asegurar la seguridad de hilos en tus aplicaciones. Con colecciones concurrentes, puedes gestionar recursos compartidos de manera efectiva, previniendo carreras de datos y colisiones entre hilos.
Integrar bibliotecas externas como IronPDF puede aumentar aún más la funcionalidad de colecciones concurrentes permitiendo la generación de informes PDF visualmente atractivos o documentos. IronPDF ofrece una prueba gratuita de su biblioteca para la conversión de HTML a PDF, permitiéndote explorar sus capacidades y opciones de licencia desde $999.

Jacob Mellor es Director de Tecnología de Iron Software y un ingeniero visionario pionero en la tecnología C# PDF. Como desarrollador original de la base de código principal de Iron Software, ha dado forma a la arquitectura de productos de la empresa desde su creación, transformándola, junto con el director ejecutivo Cameron Rimington, en una empresa de más de 50 personas que presta servicios a la NASA, Tesla y organismos gubernamentales de todo el mundo.
Artículos Relacionados


