Listas concorrentes em C# (Como funciona para desenvolvedores)
Se você já teve várias threads disputando o acesso a um recurso compartilhado, sabe que a implementação thread-safe não é brincadeira. Mas não se preocupe! O C# oferece soluções com coleções concorrentes — um conjunto poderoso de classes de coleção genéricas e thread-safe que garantem segurança de threads com estilo e elegância.
Segurança de threads e coleções concorrentes em C
Vamos começar imaginando um cruzamento movimentado em uma cidade, sem semáforos. Dá para imaginar o caos! Isso é semelhante ao que acontece quando várias threads acessam simultaneamente um recurso compartilhado sem um sistema adequado em vigor. Felizmente, em C#, temos semáforos para nossas threads — são chamadas de coleções concorrentes. São classes de coleção que permitem que apenas uma thread acesse um recurso por vez. Essa segurança de threads é crucial ao trabalhar com múltiplas threads.
Explorando coleções concorrentes thread-safe em C
Em C#, o namespace System.Collections.Concurrent possui uma variedade de classes de coleção concorrentes, como ConcurrentDictionary, ConcurrentQueue, ConcurrentStack e ConcurrentBag. Essas classes de coleções não ordenadas fornecem uma versão thread-safe de suas contrapartes não concorrentes. O que diferencia as coleções concorrentes é que elas são coleções concorrentes não ordenadas, ou seja, os elementos não possuem uma ordem específica. Por exemplo, em uma lista concorrente, você não sabe exatamente onde um item será inserido. O foco está em garantir a segurança de threads, não em manter a ordem.
Vejamos um exemplo da vida real. Imagine um campo para envio de senhas em um site. Com uma coleta simultânea, vários usuários podem enviar suas senhas ao mesmo tempo. Cada ação de 'enviar' é como uma thread, e a coleção concorrente garante que cada envio seja seguro para threads, processado de forma segura e eficaz.
Dicionário Concorrente: Um Exemplo do Mundo Real
Agora, vamos explorar a classe de coleção ConcurrentDictionary com um exemplo da vida real. Imagine uma livraria online com um recurso de recomendação. Cada clique do usuário adiciona um livro à sua lista pessoal de recomendações, representada por um dicionário. Como vários usuários navegam e clicam em livros ao mesmo tempo, temos várias threads acessando o dicionário simultaneamente.
Um ConcurrentDictionary em C# seria algo como isto:
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)()
Para adicionar um livro à coleção completa de recomendações de um usuário, podemos usar o 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 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
Nesse cenário, a classe de coleção ConcurrentDictionary garante que cada clique (ou 'thread') seja tratado individualmente, de modo que as recomendações de dois usuários não sejam misturadas. Ela lida com toda a segurança de threads, para que você não precise se preocupar com condições de corrida e outros problemas de concorrência relacionados a múltiplas threads.
Implementando operações seguras para threads
Além de TryAdd, as coleções concorrentes em C# fornecem uma variedade de outras operações thread-safe como TryRemove e TryUpdate. Esses métodos garantem que apenas uma thread possa executar uma operação por vez. Assim, por exemplo, se quiséssemos remover um livro das recomendações de um usuário no exemplo anterior, poderíamos usar o 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 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
O método TryRemove tentará remover o valor da chave fornecida (neste caso, um usuário) e colocá-lo na variável removedBook.
Copiando coleções concorrentes
Agora, digamos que você queira copiar sua coleção concorrente para uma matriz. As coleções concorrentes fornecem um método CopyTo exatamente para esse propósito:
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
Aqui, o método CopyTo copia todos os livros (valores) do dicionário concorrente recommendedBooks para o bookArray.
Coleção Thread Safe
O C# também fornece coleções thread-safe , que são projetadas para garantir o acesso seguro a recursos compartilhados em ambientes multithread. Essas coleções, como ConcurrentBag, ConcurrentQueue e ConcurrentStack, oferecem implementações thread-safe, onde várias threads podem acessar e modificar a coleção simultaneamente sem causar conflitos ou corrupção de dados.
Eles garantem consistência e integridade ao lidar com a sincronização internamente, tornando-os ideais para cenários onde uma coleção não ordenada é suficiente e a segurança de threads é de extrema importância em seus aplicativos C#.
Saiba mais sobre o IronPDF , uma biblioteca popular em C# que permite gerar documentos PDF a partir de HTML sem esforço .
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
Embora à primeira vista possa não parecer diretamente relacionado a listas concorrentes, o IronPDF pode complementar suas operações de coleta concorrente, fornecendo uma maneira fácil de criar relatórios em PDF, registros ou qualquer outro documento que capture os resultados do seu processamento concorrente.
Considere o cenário em que você tem um aplicativo multithread que realiza processamento intensivo de dados. À medida que os threads processam os dados, você pode querer capturar os resultados e gerar um relatório em PDF para análises posteriores ou para fins de registro. É aí que o IronPDF entra em ação.
Usar o IronPDF é tão simples quanto adicionar a biblioteca ao seu projeto e utilizar sua API prática. Aqui está um exemplo de como você pode integrar o IronPDF às suas operações de coleta simultânea:
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
'}
Aqui está o resultado do código:

Conclusão
Em resumo, compreender e utilizar coleções concorrentes em C#, como listas concorrentes, pode melhorar significativamente sua capacidade de lidar com cenários multithread e garantir a segurança de threads em suas aplicações. Com coleções concorrentes, você pode gerenciar recursos compartilhados de forma eficaz, evitando condições de corrida e colisões entre threads.
A integração de bibliotecas externas como o IronPDF pode ampliar ainda mais a funcionalidade de coleções simultâneas, permitindo a geração de relatórios ou documentos em PDF visualmente atraentes. A IronPDF oferece um teste gratuito de sua biblioteca para conversão de HTML para PDF , permitindo que você explore seus recursos e opções de licença a partir de $799.
Perguntas frequentes
O que são coleções concorrentes em C#?
As coleções concorrentes em C# são um conjunto de classes de coleção genéricas e thread-safe que garantem a segurança de threads quando várias threads acessam recursos compartilhados.
Por que a segurança de threads é importante em C#?
Em C#, a segurança de threads é fundamental para evitar caos e corrupção de dados quando várias threads acessam e modificam recursos compartilhados simultaneamente. Ela garante que as operações sejam executadas de forma controlada.
Como posso criar uma lista thread-safe em C#?
Embora o C# não forneça uma classe List thread-safe diretamente, você pode usar outras coleções concorrentes como `ConcurrentBag` ou `ConcurrentDictionary` para operações thread-safe semelhantes.
O que é um ConcurrentDictionary em C#?
Em C#, um ConcurrentDictionary é uma classe de coleção thread-safe pertencente ao namespace `System.Collections.Concurrent`. Ele permite que múltiplas threads adicionem, atualizem e removam pares chave-valor simultaneamente, de forma segura.
Como um ConcurrentDictionary garante a segurança de threads?
Um ConcurrentDictionary garante a segurança de threads ao lidar com a sincronização internamente, permitindo que apenas uma thread execute operações como adicionar ou remover itens por vez.
Como adicionar um item a um ConcurrentDictionary?
Você pode adicionar um item a um ConcurrentDictionary usando o método TryAdd , que tenta adicionar um par chave-valor somente se a chave ainda não existir no dicionário.
Qual é a finalidade do método CopyTo em coleções concorrentes?
O método CopyTo em coleções concorrentes é usado para copiar os elementos da coleção para uma matriz, fornecendo uma maneira de transferir dados da coleção para outro formato de armazenamento.
O IronPDF pode ser usado para gerar relatórios em PDF a partir de dados processados?
Sim, o IronPDF pode ser usado para gerar relatórios em PDF a partir de dados processados por aplicativos multithread, capturando os resultados de operações simultâneas.
De que forma o uso do IronPDF melhora a funcionalidade de operações simultâneas?
O IronPDF aprimora as operações simultâneas, permitindo a criação de documentos PDF a partir de dados processados, oferecendo uma maneira de documentar e compartilhar os resultados do processamento multithread.
Qual o papel do IronPDF em aplicações C# multithread?
O IronPDF permite que os desenvolvedores gerem relatórios em PDF a partir de dados processados em paralelo, facilitando a consolidação e o compartilhamento de resultados de operações multithread.




