AYUDA .NET

Lista concurrente en C# (Cómo funciona para desarrolladores)

Actualizado 6 de junio, 2023
Compartir:

Si alguna vez has tenido varios subprocesos disputándose el acceso a un recurso compartido, sabrás que la implementación segura de subprocesos no es un juego. Pero no te preocupes! C# le ofrece colecciones concurrentes, un potente conjunto de clases de colección genéricas y seguras para subprocesos que garantizan la seguridad de los subprocesos con estilo y elegancia.

Seguridad de hilos y colecciones concurrentes en C#

Empecemos imaginando un animado cruce urbano sin semáforos. Puedes imaginarte el caos! Esto es similar a lo que ocurre cuando varios subprocesos acceden simultáneamente a un recurso compartido sin un sistema adecuado. Afortunadamente, en C#, tenemos semáforos para nuestros hilos - se llaman colecciones concurrentes. Son clases de colección que sólo permiten que un mismo hilo acceda a un recurso a la vez. Esta seguridad es crucial cuando se trabaja con varios subprocesos.

Exploración de colecciones concurrentes seguras para hilos en 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 colecciones desordenadas proporcionan una versión segura para hilos de sus homólogas 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 se sabe exactamente dónde se inserta un elemento. La atención se centra en garantizar la seguridad de las roscas, no en mantener un orden.

Tomemos un ejemplo de la vida real. Piense en un puesto de envío de contraseñas en un sitio web. Con una recopilación concurrente, varios usuarios pueden enviar sus contraseñas simultáneamente. Cada acción de envío es como un hilo, y la colección concurrente asegura que cada envío es seguro para el hilo, procesado de forma segura y eficaz.

ConcurrentDictionary Un ejemplo real

Ahora, vamos a explorar la clase de colección ConcurrentDictionary con un ejemplo de la vida real. Imagine una librería en línea con una función de recomendación. Cada clic del usuario añade un libro a su lista personal de recomendaciones, representada por un diccionario. Como varios usuarios navegan y hacen clic en los libros al mismo tiempo, tenemos varios hilos accediendo simultáneamente al diccionario.

Un ConcurrentDictionary en C# tendría este aspecto

ConcurrentDictionary recommendedBooks = new ConcurrentDictionary();
ConcurrentDictionary recommendedBooks = new ConcurrentDictionary();
Dim recommendedBooks As New ConcurrentDictionary()
VB   C#

Para añadir un libro a toda la colección de recomendaciones de un usuario, podríamos utilizar el método Insert:

public void Insert(string user, string book)
{
    recommendedBooks.TryAdd(user, book);
}
public void Insert(string user, string book)
{
    recommendedBooks.TryAdd(user, book);
}
Public Sub Insert(ByVal user As String, ByVal book As String)
	recommendedBooks.TryAdd(user, book)
End Sub
VB   C#

En este escenario, la clase de colección ConcurrentDictionary garantiza que cada clic (o "hilo) se trata de forma individual, por lo que no se mezclan las recomendaciones de dos usuarios. Maneja toda la seguridad de los 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 como TryRemove y TryUpdate. Estos métodos aseguran que sólo un hilo puede realizar una operación a la vez. Así, por ejemplo, si quisiéramos eliminar un libro de las recomendaciones de un usuario en el ejemplo anterior, podríamos utilizar el método RemoveAt.

public void RemoveAt(string user)
{
    string removedBook;
    recommendedBooks.TryRemove(user, out removedBook);
}
public void RemoveAt(string user)
{
    string removedBook;
    recommendedBooks.TryRemove(user, out removedBook);
}
Public Sub RemoveAt(ByVal user As String)
	Dim removedBook As String = Nothing
	recommendedBooks.TryRemove(user, removedBook)
End Sub
VB   C#

El método TryRemove intentará eliminar el valor de la clave proporcionada (en este caso, un usuario) y ponerlo en la variable removedBook.

Copia de colecciones simultáneas

Ahora, digamos que quieres copiar tu colección concurrente a un array. Las colecciones concurrentes proporcionan un método CopyTo para este propósito exacto:

public void CopyTo()
{
    string [] bookArray = new string [recommendedBooks.Count];
    recommendedBooks.Values.CopyTo(bookArray, 0);
}
public void CopyTo()
{
    string [] bookArray = new string [recommendedBooks.Count];
    recommendedBooks.Values.CopyTo(bookArray, 0);
}
Public Sub CopyTo()
	Dim bookArray(recommendedBooks.Count - 1) As String
	recommendedBooks.Values.CopyTo(bookArray, 0)
End Sub
VB   C#

Aquí, el método CopyTo copia todos los libros (valores) del diccionario concurrente Libros recomendados al Array de libros.

Colección Thread Safe

C# también ofrece colecciones seguras diseñados para garantizar un acceso seguro a los recursos compartidos en entornos multihilo. Estas colecciones, como ConcurrentBag, ConcurrentQueue y ConcurrentStack, ofrecen implementaciones a prueba de hilos en las que varios hilos pueden acceder a la colección y modificarla simultáneamente sin causar conflictos ni corrupción de datos.

Garantizan la coherencia y la integridad mediante la gestión interna de la sincronización, por lo que son ideales para situaciones en las que una colección desordenada es suficiente y la seguridad de los subprocesos es de suma importancia en sus aplicaciones C#.

IronPDF es una popular biblioteca de C# que permite generar documentos PDF a partir de HTML sin esfuerzo.

using IronPdf;

class Program
{
    static void Main(string [] args)
    {
        var renderer = new ChromePdfRenderer();

        //  1. Convertir cadena HTML a 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. Convertir archivo HTML a PDF
        var htmlFilePath = "path_to_your_html_file.html"; //  Especifique la ruta a su archivo HTML
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        //  3. Convertir URL a PDF
        var url = "http://ironpdf.com"; // Especificar la URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string [] args)
    {
        var renderer = new ChromePdfRenderer();

        //  1. Convertir cadena HTML a 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. Convertir archivo HTML a PDF
        var htmlFilePath = "path_to_your_html_file.html"; //  Especifique la ruta a su archivo HTML
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        //  3. Convertir URL a PDF
        var url = "http://ironpdf.com"; // Especificar la 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. Convertir cadena HTML a 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. Convertir archivo HTML a PDF
		Dim htmlFilePath = "path_to_your_html_file.html" '  Especifique la ruta a su archivo HTML
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		'  3. Convertir URL a PDF
		Dim url = "http://ironpdf.com" ' Especificar la URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
VB   C#

Aunque al principio puede no parecer directamente relacionado con las listas concurrentes, IronPDF puede complementar sus operaciones de recolección concurrente proporcionando una forma sencilla de crear informes PDF, registros o cualquier otro documento que capture los resultados de su procesamiento concurrente.

Considere el escenario en el que tiene una aplicación multihilo que realiza un procesamiento intensivo de datos. A medida que los hilos trabajan su magia en los datos, es posible que desee capturar los resultados y generar un informe en PDF para su posterior análisis o mantenimiento de registros. Aquí es donde IronPDF entra en juego.

Utilizar IronPDF es tan sencillo como añadir la biblioteca a su proyecto y utilizar su práctica API. He aquí un ejemplo de cómo puede integrar IronPDF en sus operaciones de recogida simultánea:

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

//  Crear un diccionario concurrente para guardar los datos procesados
ConcurrentDictionary processedData = new ConcurrentDictionary();

//  Defina su lista de datos (sustitúyala por su fuente de datos real)
List dataList = GetDataList();

//  Procese sus datos simultáneamente y almacene los resultados en el diccionario
Parallel.ForEach(dataList, (dataItem) =>
{
    string processedResult = ProcessDataItem(dataItem);
    processedData.TryAdd(dataItem.Id, processedResult);
});

//  Generar un informe PDF con los datos procesados
var renderer = new ChromePdfRenderer();
var pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData));
pdfDocument.SaveAs("C:\\processed_data_report.pdf");

//  Método para recuperar la lista de datos (sustitúyalo por la lógica de su fuente de datos real)
List GetDataList()
{
    List dataList = new List()
    {
        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;
}

//  Método para procesar cada dato y devolver el resultado (sustitúyalo por su lógica real de procesamiento de datos)
string ProcessDataItem(DataItem dataItem)
{
    //  Simulación de tratamiento de datos con retraso
    Task.Delay(100).Wait();
    return $"Processed: {dataItem.Name}";
}

//  Método para crear el informe HTML utilizando los datos procesados (sustitúyalo por su propia lógica de elaboración de informes)
string BuildHtmlReport(ConcurrentDictionary processedData)
{
    string html = "Processed Data Report";
    foreach (var kvp in processedData)
    {
        html += $"Item {kvp.Key}: {kvp.Value}";
    }
    html += "";
    return html;
}

//  Clase de marcador de posición para el elemento de datos (sustitúyala por la clase real del elemento de datos)
public class DataItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    //  Añada otras propiedades según sea necesario
}
using IronPdf;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

//  Crear un diccionario concurrente para guardar los datos procesados
ConcurrentDictionary processedData = new ConcurrentDictionary();

//  Defina su lista de datos (sustitúyala por su fuente de datos real)
List dataList = GetDataList();

//  Procese sus datos simultáneamente y almacene los resultados en el diccionario
Parallel.ForEach(dataList, (dataItem) =>
{
    string processedResult = ProcessDataItem(dataItem);
    processedData.TryAdd(dataItem.Id, processedResult);
});

//  Generar un informe PDF con los datos procesados
var renderer = new ChromePdfRenderer();
var pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData));
pdfDocument.SaveAs("C:\\processed_data_report.pdf");

//  Método para recuperar la lista de datos (sustitúyalo por la lógica de su fuente de datos real)
List GetDataList()
{
    List dataList = new List()
    {
        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;
}

//  Método para procesar cada dato y devolver el resultado (sustitúyalo por su lógica real de procesamiento de datos)
string ProcessDataItem(DataItem dataItem)
{
    //  Simulación de tratamiento de datos con retraso
    Task.Delay(100).Wait();
    return $"Processed: {dataItem.Name}";
}

//  Método para crear el informe HTML utilizando los datos procesados (sustitúyalo por su propia lógica de elaboración de informes)
string BuildHtmlReport(ConcurrentDictionary processedData)
{
    string html = "Processed Data Report";
    foreach (var kvp in processedData)
    {
        html += $"Item {kvp.Key}: {kvp.Value}";
    }
    html += "";
    return html;
}

//  Clase de marcador de posición para el elemento de datos (sustitúyala por la clase real del elemento de datos)
public class DataItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    //  Añada otras propiedades según sea necesario
}
Imports IronPdf
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks

'  Crear un diccionario concurrente para guardar los datos procesados
Private processedData As New ConcurrentDictionary()

'  Defina su lista de datos (sustitúyala por su fuente de datos real)
Private dataList As List = GetDataList()

'  Procese sus datos simultáneamente y almacene los resultados en el diccionario
Parallel.ForEach(dataList, Sub(dataItem)
	Dim processedResult As String = ProcessDataItem(dataItem)
	processedData.TryAdd(dataItem.Id, processedResult)
End Sub)

'  Generar un informe PDF con los datos procesados
Dim renderer = New ChromePdfRenderer()
Dim pdfDocument = renderer.RenderHtmlAsPdf(BuildHtmlReport(processedData))
pdfDocument.SaveAs("C:\processed_data_report.pdf")

'  Método para recuperar la lista de datos (sustitúyalo por la lógica de su fuente de datos real)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'List GetDataList()
'{
'	List dataList = New List() { 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;
'}

'  Método para procesar cada dato y devolver el resultado (sustitúyalo por su lógica real de procesamiento de datos)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'string ProcessDataItem(DataItem dataItem)
'{
'	'  Simulación de tratamiento de datos con retraso
'	Task.Delay(100).Wait();
'	Return string.Format("Processed: {0}", dataItem.Name);
'}

'  Método para crear el informe HTML utilizando los datos procesados (sustitúyalo por su propia lógica de elaboración de informes)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'string BuildHtmlReport(ConcurrentDictionary processedData)
'{
'	string html = "Processed Data Report";
'	foreach (var kvp in processedData)
'	{
'		html += string.Format("Item {0}: {1}", kvp.Key, kvp.Value);
'	}
'	html += "";
'	Return html;
'}

'  Clase de marcador de posición para el elemento de datos (sustitúyala por la clase real del elemento de datos)
'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;
'	}
'	'  Añada otras propiedades según sea necesario
'}
VB   C#

Este es el resultado del código:

Lista concurrente C# (Cómo funciona para los desarrolladores) Figura 1 - Salida

Conclusión

En conclusión, comprender y utilizar las colecciones concurrentes de C#, como las listas concurrentes, puede mejorar enormemente su capacidad para manejar escenarios multihilo y garantizar la seguridad de los hilos en sus aplicaciones. Las colecciones concurrentes permiten gestionar eficazmente los recursos compartidos, evitando las carreras de datos y las colisiones entre subprocesos.

La integración de bibliotecas externas como IronPDF puede aumentar aún más la funcionalidad de las colecciones concurrentes al permitir la generación de informes o documentos PDF visualmente atractivos. IronPDF ofrece un prueba gratuita que le permite explorar sus capacidades, y opciones de licencia a partir de $749.

< ANTERIOR
Qué es Visual Studio (cómo funciona para los desarrolladores)
SIGUIENTE >
Vim para Windows (Cómo funciona para desarrolladores)

¿Listo para empezar? Versión: 2024.7 recién publicada

Descarga gratuita de NuGet Descargas totales: 9,974,197 Ver licencias >
123