Quick Reference
먼저 들어온 작업부터 처리하면 Queue<T>(FIFO), 가장 최근 상태부터 되돌리면 Stack<T>(LIFO), 숫자나 비교자로 우선순위를 정하면 PriorityQueue<TElement, TPriority>를 씁니다. 비어 있을 수 있는 큐와 스택에서는 예외를 던지는 Dequeue/Pop 대신 Try... 계열을 사용합니다.
var jobs = new Queue<string>();
jobs.Enqueue("import");
if (jobs.TryDequeue(out string? job))
Process(job);
var undo = new Stack<string>();
undo.Push("rename");
if (undo.TryPop(out string? action))
Undo(action);
var scheduled = new PriorityQueue<string, int>();
scheduled.Enqueue("urgent", priority: 0);Peek과TryPeek은 원소를 보지만 제거하지 않습니다.- 일반
Queue<T>와Stack<T>는 여러 스레드가 동시에 수정하는 용도가 아닙니다. PriorityQueue는 같은 우선순위의 삽입 순서를 보장하지 않습니다.
Queue와 Stack의 호출
Queue<T>에서는 Enqueue로 뒤에 넣고 Dequeue로 앞에서 꺼냅니다. Stack<T>에서는 Push로 위에 넣고 Pop으로 위에서 꺼냅니다. 빈 경우 Dequeue, Peek, Pop은 InvalidOperationException을 던집니다.
var queue = new Queue<int>([10, 20]);
int next = queue.Dequeue(); // 10
int look = queue.Peek(); // 20, 유지
bool found = queue.TryPeek(out int queuedValue);
var stack = new Stack<int>();
stack.Push(10);
stack.Push(20);
int latest = stack.Pop(); // 20
bool popped = stack.TryPop(out int previous);작업 대기열, BFS는 FIFO가 필요한 Queue<T>에 맞고, undo 기록, 괄호 검사, DFS는 LIFO가 필요한 Stack<T>에 맞습니다. 둘 모두 열거 중 추가·제거를 지원하는 컬렉션이 아니므로 순회와 변경을 섞지 않습니다.
PriorityQueue의 우선순위
.NET 6부터 제공되는 PriorityQueue<TElement, TPriority>는 기본 비교자로 가장 작은 우선순위를 먼저 꺼냅니다. 요소와 우선순위는 별개이며, TryDequeue로 둘을 함께 받을 수 있습니다.
var queue = new PriorityQueue<string, int>();
queue.Enqueue("normal", 10);
queue.Enqueue("urgent", 0);
if (queue.TryDequeue(out string? item, out int priority))
Console.WriteLine($"{priority}: {item}"); // 0: urgent가장 큰 숫자를 먼저 처리해야 하면 우선순위 값을 반대로 만들거나 IComparer<TPriority>를 생성자에 전달합니다. 동점의 안정된 순서가 필요하면 (priority, sequence) 같은 우선순위 타입에 증가하는 순번을 포함합니다. UnorderedItems는 디버깅이나 복사용 뷰이며, 우선순위 순으로 정렬된 목록이 아닙니다.
EnqueueDequeue와 DequeueEnqueue는 새 요소 하나를 넣고 최소 요소 하나를 처리하는 반복 작업에서 쓸 수 있습니다. 어느 요소가 반환되고 어떤 요소가 남는지 이름만으로 추측하지 말고, 단위 테스트로 작업 규칙을 고정합니다.
동시성 및 비동기 작업
여러 생산자와 소비자가 개별 항목을 넣고 꺼내야 하면 ConcurrentQueue<T>의 Enqueue와 TryDequeue를 씁니다. 이 컬렉션은 개별 연산을 스레드 안전하게 만들지만, “비어 있을 때까지 기다리기”, 처리량 제한, 완료 신호를 제공하지는 않습니다.
var pending = new ConcurrentQueue<string>();
pending.Enqueue("thumbnail");
if (pending.TryDequeue(out string? work))
Process(work);비동기 생산자-소비자 흐름에서 대기와 backpressure가 필요하면 System.Threading.Channels.Channel<T>가 더 맞습니다. ConcurrentQueue<T> 위에서 while로 반복 폴링하면 유휴 CPU를 쓰거나 종료 조건을 잃기 쉽습니다.
자주 틀리는 부분
- 빈 큐와 스택을 정상적인 상태로 다룰 때는
Count > 0검사 또는Try...API를 사용합니다. PriorityQueue는 priority가 낮을수록 먼저 나옵니다. “높음”이라는 업무 용어와 숫자 방향을 코드에서 명확히 맞춥니다.- 동일 우선순위의 FIFO를 기대하지 않습니다. 순서가 필요하면 우선순위에 순번을 포함합니다.
ConcurrentQueue<T>만으로 복수 단계의 검사·처리를 원자적으로 만들 수는 없습니다.
참고 링크
4 sources