Queue (非泛型) 與 Queue<T> (泛型) 皆非線程安全。多執行緒同時 Enqueue/Dequeue 或枚舉時修改,會導致資料損毀或 InvalidOperationException。
var queue = new Queue();
queue.Enqueue("A"); queue.Enqueue("B");
var syncQueue = Queue.Synchronized(queue); // 回傳同步包裝器
Console.WriteLine($"Original: {queue.IsSynchronized}"); // False
Console.WriteLine($"Wrapped: {syncQueue.IsSynchronized}"); // True
// 使用:單一操作原子化
syncQueue.Enqueue("C");
var item = syncQueue.Dequeue();
// 枚舉仍需手動鎖定!
lock (syncQueue.SyncRoot)
{
foreach (var x in syncQueue) Console.WriteLine(x);
}
var queue = new Queue();
private static readonly object _lock = new object();
// 生產者
Task.Run(() => {
for (int i = 0; i < 100; i++)
{
lock (_lock) queue.Enqueue($"Item-{i}");
Thread.Sleep(10);
}
});
// 消費者
Task.Run(() => {
while (true)
{
string item = null;
lock (_lock)
{
if (queue.Count > 0) item = queue.Dequeue();
}
if (item != null) Console.WriteLine($"Processed: {item}");
else Thread.Sleep(50);
}
});
using System.Collections.Concurrent;
using System.Threading;
public class BoundedQueue
{
private readonly ConcurrentQueue _queue = new();
private readonly SemaphoreSlim _slots; // 可用空位
private readonly SemaphoreSlim _items; // 可用項目
public BoundedQueue(int capacity)
{
_slots = new SemaphoreSlim(capacity, capacity);
_items = new SemaphoreSlim(0, capacity);
}
public void Enqueue(T item)
{
_slots.Wait(); // 等待空位
_queue.Enqueue(item);
_items.Release(); // 通知有新項目
}
public T Dequeue()
{
_items.Wait(); // 等待項目
_queue.TryDequeue(out var item);
_slots.Release(); // 釋放空位
return item;
}
public bool TryDequeue(out T item, TimeSpan timeout)
{
if (_items.Wait(timeout))
{
_queue.TryDequeue(out item);
_slots.Release();
return true;
}
item = default;
return false;
}
}
| 類別 | 特性 | 適用場景 |
|---|---|---|
ConcurrentQueue<T> | 無鎖、高併發 Enqueue/TryDequeue、無界 | 高吞吐、無界緩衝、多生產多消費 |
BlockingCollection<T> | 包裝 ConcurrentQueue、支援界限/阻塞/CompleteAdding | 生產者消費者、背壓控制、優雅關閉 |
Channel<T> (.NET Core 3.0+) | 非同步優先、支援等待/取消/彈性緩衝 | 非同步管道、高效能資料流 |
Queue<T> + lock | 簡單、可控、支援 foreach (需鎖) | 低併發、需自訂邏輯、教學用途 |
Queue.Synchronized | 單一方法原子、枚舉仍需手動鎖 | 舊代碼遷移、簡單場景 |
Queue.Synchronized 保護枚舉 → 不保護,必須 lock (syncQueue.SyncRoot)ConcurrentQueue 用 Count 判斷再 TryDequeue → 競態條件,直接 TryDequeueBlockingCollection 未呼叫 CompleteAdding() → 消費者永遠阻塞foreach 未鎖定 → InvalidOperationException
// 生產者消費者標準模式 (.NET 4.0+)
var queue = new BlockingCollection(boundedCapacity: 1000);
// 生產者 (多執行緒)
Parallel.For(0, 100, i => queue.Add(new WorkItem(i)));
// 完成生產
queue.CompleteAdding();
// 消費者 (單/多執行緒)
foreach (var item in queue.GetConsumingEnumerable())
{
Process(item);
}
理解 Queue 線程安全本質,選對工具,才能寫出高效、正確的併發程式。