프로덕션 코드에서 스레드를 중단시키지 말자

 

프로덕트 코드에서는, 결과를 예측할 수 없고 프로그램을 불안정하게 만드는 스레드 강제 중단의 사용을 피하라.

 

 Thread 개체에서 제공하는 Abort() 메소드는 스레드를 제거하는 기능을 제공한다. 그러려고 Abort()는 런타임을 이용해서 대상 스레드 내에서 ThreadAbortException 예외가 발생하게 한다. 이 예외를 catch할 수 있지만, catch해서 무시하도록 처리한다고 해도 스레드가 실제로 제거되었는지 확인할 때 다시 발생된다. 이렇게 스레드를 강제로 중단하는 것이 바람직하지 않은 수많은 이유 가운데 몇 가지를 들어보면 다음과 같다.

l  이 메소드는 스레드를 중단시키는 시도를 할 뿐이지 그 시도의 성공을 보장하지 않는다. 예를 들어 스레드 내의 제어점이 현재 finally 블록(방해해서는 안 되는 리소스 해제를 위한 중요한 코드를 실행중일 수 있기 때문) 혹은 비관리 코드(비관리 코드 내부에서 중단 시킬 경우  CLR 자체에 문제가 생길 수 있다) 내부에 있는 경우 런타임이 ThreadAbortException을 발생시키지 않는다. 대신 런타임은 제어가 finally 블록을 벗어나거나 관리 코드로 반환되기를 기다렸다가 예외를 발생시킨다. 하지만 반드시 이렇게 처리된다는 보장이 없다. 중단하려는 스레드의 finally 블록에 무한 루프가 있을 수 있고, 역설적으로 애초에 finally 문의 무한 루프 때문에 스레드를 중단하려고 했을 수도 있다.

l  중단된 스레드가 lock 문으로 보호되는 임계 코드 내에 있을 수 있다. finally와 달리 lock 문은 예외를 막지 않는다. 따라서 진행 중인 임계 코드는 예외를 맞아 실행 중에 중단되고 lock 개체는 자동으로 해체되면서 임계 영역에 들어가려던 다른 코드의 진입이 허용된다. 이때 실행되다가 중단된 코드의 상태가 임계 영역에 그대로 남아 있게 되는데, 근본적으로 잠금이라는 개념을 이용하는 이유가 이런 상태(원자성을 제공하지 못하는 상태)를 막기 위한 것이라는 점을 떠올린다면 이것은 분명 잘못된 것이며, 스레드를 중단함으로 인해 스레드에 안전한 코드를 한 순간에 위험한 코드로 만들어 버릴 수 있다.

l  CLR은 스레드의 중단으로부터 내부 데이터 구조가 안전하게 보홈되을 보장하지만 BCL(Base Class Liabrary)의 경우는 그렇지 못하다. 따라서 운 나쁘게 좋지 않은 시점에 예외로 인해 스레드가 중단된다면 개발자가 애써 구축한 데이터 구조나 BCL에서 제공하는 데이터 구조는 망가질 수 있다. 다른 스레드의 코드나 중단되는 스레드의 finally 블록에 있는 코드에서 망가진 데이터를 참조할 수 있기 때문에 오류가 확산될 수 있다.

 

결론적으로 최후 수단이 아니라면 스레드를 강제 중단시켜서는 안 된다. , 전체 어플리케이션 도메인 혹은 프로세스를 비상 종료하는 경우의 일환으로 중단하는 경우에만 사용하는 것이 이상적이다. 다행히 태스크 기반의 비동기 방식은 스레드 강제 종료를 위한 더 단단하고 안전한 취소 패턴을 이용하고 있으며 뒤에서 소개할 비동기 작업 절에서 살펴본다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

+ Recent posts