본문 바로가기
개발언어/C#

[C#] Queue VS ConcurrentQueue

by 창용이랑 2021. 2. 17.
728x90

알게된 배경

UDP통신을 수신해야 하는데, 스레드 하나로는 프로그램의 성능을 올릴 수 없으니 당연히 멀티 스레드로 처리를 해야한다. (이는 UDP 프로토콜은 오버플러워되거나 에러가 난 패킷은 가차없이 폐기 처분하기 때문에 발생되는 문제를 막기 위함이다.) 문제는 이런 스레드가 작동되는 영역들간에 데이터 공유혹은 전달 할 수 있는 자료가 필요로 하게 된다. 이를 사용할 자료구조가 Queue였다.

 물론 링크리스트로도 충분히 구현을 할 수 도 있다.

 

큐의 개요

 많이 비교되는 자료구조는 스택이다. 스택은 먼저들어간게 나중에 나오는 구조(FILO or LIFO)인데 반해 큐는 들어간 순서대로 나오는 실제 세게에서 줄 서 있는 것과 같다. 추상적으로 생각을 하면, 우리가 일상중 머릿속에서 작업스케줄을 짜는 것과 같다.

 위의 예시처럼 실제로도 큐는 작업 스케줄 처럼 많이 사용되기도 한다. 큐의 종류는 선 큐, 환 큐, 링크드 큐로 분류가 되는데, 선 큐의 경우 오버플러워의 위험이 크기 때문에 잘 사용되지 않는다. 닷넷 한정으로 넣는 것을 Enqueue, 빼는 것을 Dequeue로 구현이 되어 있다.

 

닷넷 Queue 구조

 닷넷에서 제공하는 큐(Queue)는 환 큐에 속한다. 또한 성능을 향상시키기 위해서 정적 배열을 기반으로 하고 있다.

문제점

Queue가 비어 있을 경우 Dequeue를 시도하면 예외를 발생시킨다. 그래서 일반적으로 큐를 사용할 때 Count를 검사해서 0 이상일 때 Dequeue 사용하도록 구현을 한다. 다만, 설계시 멀티스레드에 대해서 생각을 못 한 것인지 다수의 스레드의 동시 접근시 문제가 발생된다. 때문에 Enqueue와 Dequeue에 크리티컬 섹션으로 설정해야 한다(lock을 구현해야함).

 이론상 2개의 스레드중 하나는 Enqueue만하고 다른 하나는 Dequeue만 하면, 문제가 없을 것 같지만, count등 내부적으로 공유되는 값으로 인해 간헐적인 값의 오류가 발생한다. 이러한 오류는 에러를 잘 발생시키지는 않지만, 의도한것과 다른 값을 결과로 내기 때문에 위험하다.

Queue 클래스

 Queue<T>와 달리 Queue클래스가 추가로 존재하는데, 기능이 Queue<T>보다는 많지만, 동일한 문제점이 있다.

 

닷넷 ConcurrentQueue 구조

 닷넷 4.0 부터 지원하는 구조이다. 기존 Queue 구조의 멀티스레드로 인한 문제가 있어서 이러한 단점을 해결하기 위한 구조이다. 기존 Queue와 큰 차이점은 인스턴스화 할때 크기를 선언하지 않는다. 이때문에 링크드 큐인것으로 생각된다.

 Enqueue는 기존 Queue와 비슷하게 사용되지만, Dequeue 대신에 TryDequeue를 사용해서 자료를 반환한다. 메서드 이름에서 알 수 있듯이 Enqueue과정이 Dequeue보다 우선 순위가 높게 구현이 되어있다. 또한 Dequeue과정이 성공/실패 여부를 반환한다. 이를 통해서 Count에 접근할 필요가 없어졌다.

 

유니티에서 ConcurrentQueue

 유니티에서는 닷넷을 사용할 경우 2.0에 해당되기 때문에 공식적으로 ConcurrentQueue를 사용할 수 없다. lock을 활용하여 크리티컬 섹션을 잘 설정하자.

 

참조 자료

스택오버 플로워

MSDN Queue

MSDN ConcurrentQueue



출처: https://karl27.tistory.com/66 [불확정한 세상]