Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

Thread-Safe Concurrent Collections ใน C#

ภาพรวม

Namespace System.Collections.Concurrent จัดเตรียมคอลเลกชันที่ Thread-Safe และ Scalable สำหรับ Multi-threaded Applications

คอลเลกชันหลัก

ClassTypeUse CaseKey Methods
ConcurrentDictionary<TKey,TValue>DictionaryThread-safe key-value store, CacheGetOrAdd, AddOrUpdate, TryGetValue, TryRemove
ConcurrentQueue<T>FIFO QueueProducer-Consumer, Task QueueEnqueue, TryDequeue, TryPeek
ConcurrentStack<T>LIFO StackObject Pooling, Undo OperationsPush, TryPop, TryPeek, PushRange, PopRange
ConcurrentBag<T>Unordered BagThread-local AggregationAdd, TryTake, TryPeek
BlockingCollection<T>WrapperBounded Buffer, Producer-Consumer with BlockingAdd, Take, TryAdd, TryTake, CompleteAdding, GetConsumingEnumerable
IProducerConsumerCollection<T>InterfaceCustom Concurrent CollectionsTryAdd, TryTake, ToArray

ตัวอย่าง: ConcurrentDictionary


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

public class Demo {
    static ConcurrentDictionary<string, int> _counts = new ConcurrentDictionary<string, int>();

    public static void Main() {
        Parallel.For(0, 1000, i => {
            string key = $"Key{i % 10}";
            // Atomic Add/Update
            _counts.AddOrUpdate(key, 1, (k, v) => v + 1);
        });

        foreach (var kvp in _counts.OrderBy(x => x.Key)) {
            Console.WriteLine($"{kvp.Key}: {kvp.Value}");
        }
    }
}

ตัวอย่าง: ConcurrentStack


using System;
using System.Collections.Concurrent;

class Demo {
    static void Main() {
        var stack = new ConcurrentStack<int>();
        stack.Push(50);
        stack.Push(100);
        stack.Push(150);

        Console.WriteLine($"Is Empty: {stack.IsEmpty}"); // False

        if (stack.TryPop(out int result)) {
            Console.WriteLine($"Popped: {result}"); // 150
        }

        if (stack.TryPeek(out int peeked)) {
            Console.WriteLine($"Peeked: {peeked}"); // 100
        }

        // Push/Pop Range (Efficient for batches)
        int[] items = { 1, 2, 3, 4, 5 };
        stack.PushRange(items);
        int[] popped = new int[3];
        int count = stack.TryPopRange(popped);
        Console.WriteLine($"Popped {count} items: {string.Join(", ", popped.Take(count))}");
    }
}

ตัวอย่าง: BlockingCollection (Producer-Consumer)


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

var queue = new BlockingCollection<int>(boundedCapacity: 5);

// Producer
Task.Run(() => {
    for (int i = 0; i < 10; i++) {
        queue.Add(i); // Blocks if full
        Console.WriteLine($"Produced: {i}");
        Thread.Sleep(100);
    }
    queue.CompleteAdding();
});

// Consumer
Task.Run(() => {
    foreach (var item in queue.GetConsumingEnumerable()) {
        Console.WriteLine($"Consumed: {item}");
        Thread.Sleep(200);
    }
}).Wait();

การเลือกใช้

  • ConcurrentDictionary: Cache, Shared State, Lookup by Key
  • ConcurrentQueue: FIFO Task Queue, Message Passing
  • ConcurrentStack: LIFO, Object Pooling, Undo
  • ConcurrentBag: Unordered, High Contention, Thread-local Aggregation
  • BlockingCollection: Bounded Buffer, Backpressure, Producer-Consumer Pattern

Performance Notes

  • ใช้ Lock-free / Fine-grained Locking / SpinWait
  • หลีกเลี่ยงการ Lock เองบน Concurrent Collections
  • Try* Methods ไม่ Throw Exception คืน bool แทน