푸터 콘텐츠로 바로가기
.NET 도움말

C# Concurrent List (How it Works for Developers)

If you've ever had multiple threads jostling for access to a shared resource, you know that thread-safe implementation is not a game. Don't worry, though! C# has you covered with concurrent collections - a powerful suite of thread-safe, generic collection classes that ensure thread safety with style and grace.

Thread Safety and Concurrent Collections in C#

Let's start by picturing a bustling city intersection with no traffic lights. You can imagine the chaos! This is similar to what happens when multiple threads concurrently access a shared resource without a proper system in place. Thankfully, in C#, we have traffic lights for our threads - these are called concurrent collections. They are collection classes that allow only one thread to access a resource at a time. This thread safety is crucial when working with multiple threads.

Exploring Concurrent Thread Safe Collections in C#

In C#, the namespace System.Collections.Concurrent has a variety of concurrent collection classes, like ConcurrentDictionary, ConcurrentQueue, ConcurrentStack, and ConcurrentBag. These unordered collection classes provide a thread-safe version of their non-concurrent counterparts. What sets concurrent collections apart is that they are unordered concurrent collections, meaning elements do not have a specific order. For instance, with a concurrent list, you don't know exactly where an item is inserted. The focus is on ensuring thread safety, not on maintaining an order.

Let's take a real-life example. Think of a password submit post on a website. With a concurrent collection, multiple users can submit their passwords simultaneously. Each 'submit' action is like a thread, and the concurrent collection ensures that each submission is thread-safe, processed safely, and effectively.

ConcurrentDictionary: A Real-World Example

Now, let's explore the ConcurrentDictionary collection class with a real-life example. Picture an online bookstore with a recommendation feature. Each user's click adds a book to their personal recommendation list, represented by a dictionary. As multiple users browse and click books at the same time, we have multiple threads concurrently accessing the dictionary.

A ConcurrentDictionary in C# would look something like this:

using System.Collections.Concurrent;

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

ConcurrentDictionary<string, string> recommendedBooks = new ConcurrentDictionary<string, string>();
$vbLabelText   $csharpLabel

To add a book to a user's entire collection of recommendations, we could use the TryAdd method:

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

In this scenario, the ConcurrentDictionary collection class ensures that every click (or 'thread') is dealt with individually, so no two users' recommendations get mixed up. It handles all the thread safety, so you don't have to worry about data races and other concurrency issues related to multiple threads.

Implementing Thread Safe Operations

Other than TryAdd, concurrent collections in C# provide a variety of other thread-safe operations like TryRemove and TryUpdate. These methods ensure that only one thread can perform an operation at a time. So, for instance, if we wanted to remove a book from a user's recommendations in the previous example, we could use the TryRemove method:

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

The TryRemove method will attempt to remove the value of the provided key (in this case, a user) and put it into the removedBook variable.

Copying Concurrent Collections

Now, let's say you want to copy your concurrent collection to an array. Concurrent collections provide a CopyTo method for this exact purpose:

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

Here, the CopyTo method copies all the books (values) from the recommendedBooks concurrent dictionary into the bookArray.

Thread Safe Collection

C# also provides thread-safe collections, which are designed to ensure safe access to shared resources in multithreaded environments. These collections, such as ConcurrentBag, ConcurrentQueue, and ConcurrentStack, offer thread-safe implementations where multiple threads can access and modify the collection concurrently without causing conflicts or data corruption.

They guarantee consistency and integrity by handling the synchronization internally, making them ideal for scenarios where an unordered collection is sufficient, and thread safety is of utmost importance in your C# applications.

Learn More About IronPDF is a popular C# library that allows you to generate PDF documents from HTML effortlessly.

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");
    }
}
$vbLabelText   $csharpLabel

While it may not seem directly related to concurrent lists at first, IronPDF can complement your concurrent collection operations by providing an easy way to create PDF reports, logs, or any other document that captures the results of your concurrent processing.

Consider the scenario where you have a multithreaded application that performs intensive data processing. As the threads work their magic on the data, you might want to capture the results and generate a PDF report for further analysis or record-keeping. This is where IronPDF comes into play.

Using IronPDF is as simple as adding the library to your project and utilizing its convenient API. Here's an example of how you can integrate IronPDF with your concurrent collection operations:

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

Here is the output of the code:

C# Concurrent List (How It Works For Developers) Figure 1 - Output

Conclusion

In conclusion, understanding and utilizing C# concurrent collections, such as concurrent lists, can greatly enhance your ability to handle multi-threading scenarios and ensure thread safety in your applications. With concurrent collections, you can manage shared resources effectively, preventing data races and collisions between threads.

Integrating external libraries like IronPDF can further augment the functionality of concurrent collections by enabling the generation of visually appealing PDF reports or documents. IronPDF offers a free trial of its library for HTML to PDF conversion, allowing you to explore its capabilities, and license options starting from $799.

자주 묻는 질문

C#에서 동시 컬렉션이란 무엇인가요?

C#의 동시 컬렉션은 여러 스레드가 공유 리소스에 액세스할 때 스레드 안전을 보장하는 스레드 안전 일반 컬렉션 클래스 모음입니다.

C#에서 스레드 안전이 중요한 이유는 무엇인가요?

스레드 안전은 여러 스레드가 동시에 공유 리소스에 액세스하고 수정할 때 혼란과 데이터 손상을 방지하기 위해 C#에서 매우 중요합니다. 스레드 안전은 작업이 제어된 방식으로 실행되도록 보장합니다.

C#에서 스레드 안전 목록을 만들려면 어떻게 해야 하나요?

C#은 스레드에 안전한 List 클래스를 직접 제공하지는 않지만, 유사한 스레드 안전 연산을 위해 `ConcurrentBag` 또는 `ConcurrentDictionary`와 같은 다른 동시 컬렉션을 사용할 수 있습니다.

C#에서 ConcurrentDictionary란 무엇인가요?

C#의 ConcurrentDictionary는 `System.Collections.Concurrent` 네임스페이스 내의 스레드 안전 컬렉션 클래스입니다. 여러 스레드가 동시에 키-값 쌍을 안전하게 추가, 업데이트 및 제거할 수 있습니다.

ConcurrentDictionary는 어떻게 스레드 안전을 보장하나요?

동시 사전은 내부적으로 동기화를 처리하여 한 번에 하나의 스레드만 항목 추가 또는 제거와 같은 작업을 수행할 수 있도록 함으로써 스레드 안전을 보장합니다.

ConcurrentDictionary에 항목을 추가하려면 어떻게 해야 하나요?

키가 사전에 아직 없는 경우에만 키-값 쌍을 추가하려고 시도하는 TryAdd 메서드를 사용하여 ConcurrentDictionary에 항목을 추가할 수 있습니다.

동시 컬렉션에서 CopyTo 메서드의 목적은 무엇인가요?

동시 컬렉션의 CopyTo 메서드는 컬렉션의 요소를 배열로 복사하는 데 사용되어 컬렉션에서 다른 스토리지 형식으로 데이터를 전송하는 방법을 제공합니다.

처리된 데이터에서 PDF 보고서를 생성하는 데 IronPDF를 사용할 수 있나요?

예, IronPDF를 사용하면 멀티 스레드 애플리케이션에서 처리된 데이터에서 PDF 보고서를 생성하여 동시 작업의 결과를 캡처할 수 있습니다.

IronPDF를 사용하면 동시 작업의 기능이 어떻게 향상되나요?

IronPDF는 처리된 데이터에서 PDF 문서를 생성하여 멀티스레드 처리 결과를 문서화하고 공유할 수 있는 방법을 제공함으로써 동시 작업을 향상시킵니다.

IronPDF는 멀티스레드 C# 애플리케이션에서 어떤 역할을 하나요?

IronPDF를 사용하면 개발자가 병렬로 처리된 데이터에서 PDF 보고서를 생성할 수 있으므로 멀티스레드 작업의 결과를 더 쉽게 통합하고 공유할 수 있습니다.

커티스 차우
기술 문서 작성자

커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다.

커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다.