여러 책들이나 문서들을 읽어 보면 쓰레드를 사용하면 동시성(concurrency)이 증가되지만 복잡도 역시 증가되어
코드가 어려워지고 버그를 찾기 어려워진다고들 한다. 제군들 역시 쓰레드는 좀 다뤄 보았을 것이다. 정말 쓰레드가 어렵게
느껴진 적이 얼마나 되는가?
심심한 필자, 쓰레딩(threading)과 동기화(synchronization)에 대해 재미있는(?) 퀴즈를 하나
내보려고 한다. 본인이 한 코딩 한다는 사람은 이 퀴즈에 도전(?)해 보기 바란다.
Quiz: Synchronization Bug
다 필요 없고 먼저 코드부터 까 보자. 우리 코드 쟁의들한테는 백마디 설명보다 한 두 줄의 코드가 의사 소통하는데
좋지 아니한가?
1
// 키/값을 검색해주는 간단한 예제 클래스
2
public
static
class
LookupTableBug
3
{
4
// 내부 테이블(해시테이블 이용)
5
private
static
readonly
Hashtable _Table =
new
Hashtable();
6
7
// 주어진 키에 대응되는 값을 찾아 반환한다.
8
public
static
object Lookup(object
key)
9
{
10
object val = _Table[key];
11
if (val ==
null) {
12
lock (_Table.SyncRoot)
{
13
val = PublishValue(key);
14
_Table[key] = val;
15
}
16
}
17
return val;
18
}
19
20
// 주어진 키에 대한 값을 DB, 파일, 웹 서비스 호출
등에서
21
// 읽어 테이블에 추가한다.
22
private
static
object PublishValue(object
key)
23
{
24
return
"Data";
25
}
26
}
리스트1. 키-값 검색을 하는 룩업(lookup) 테이블 클래스
위 코드의 LookupTable 클래스는 실무에서 종종 사용되는 클래스 패턴이다.
어떤 키 값에 의해 검색되는 데이터를 찾기 쉽게 해주는 클래스로서 데이터가 DB나 파일 혹은 원격 컴퓨터에서 웹 서비스
등의 방법을 통해 읽어 들어야 할 때 유용하다. 한 방에 미리 테이블을 구축할 수도 있지만
캐시 개념을 적용하여 필요할 때에만 데이터를 읽고 이것을 캐시 해 두는 기법 역시
많이 사용되곤 한다.
어찌 되었건 리스트1 코드를 살펴보면 동시에 여러 쓰레드가 Lookup
메쏘드를 호출하는 것에 대비하여 lock 키워드를 사용하여 나름대로 동기화도 수행했다. 오오... 또 한 가지 리스트1에서
신경 쓴 흔적은 성능을 위해 Lookup 메쏘드 전체에 대해 잠금을 수행하지 않았다는 점이다. 만약 동기화 문제를 피하려고
lock 키워드를 10 라인부터 16 라인까지 묶어 버리면 동기화 문제는 없겠지만 대신 테이블을 검색하는
쓰레드는 오직 하나 밖에 될 수 없고 이는 심각(?)한 동시성(concurrency) 문제에 부딪칠 수도 있다.
때문에... 테이블을 읽는 시점(10 라인)에서는 동기화가 필요하지 않았고 해당 데이터가 없는 시점(11 라인)에서
테이블에 데이터를 추가할 때 2개 이상의 쓰레드가 PublishValue를 호출하지 않도록 잠금(lock)을 사용한
것이다.
리스트1 코드는 심각하지 않지만 하나의 버그를 가지고 있다.
이 버그는 경우에 따라 심각할 수도 있지만 리스트1 의 경우라면 그다지 심각하지 않을 수도 있는 버그이다. 오늘의 퀴즈가
요것이 되겠다. (애들은 가라~~) 다양한 쓰레드 동기화 문제가 있지만, 이런 패턴은 상당히 자주 등장하는 패턴이므로
알아두면 좋겠다는 생각에 퀴즈를 내 본 것이다.
뭐 이젠 필자가 별 짓을 다한다고 생각할 지도 모르겠다. 걍 심심해서 몇 자 적어 본 것이니 버그에 대해 알고 있는
독자는 피드백을 달기 바란다. 물론 필자가 아는 버그 외에 다른 버그가 존재할 수도 있겠다. 그런 것도 좋으니 몇 마디
남겨보기 바란다.
필자가 제시하는 정답은 다음 글에 올리도록 하겠다. 텨텨텨