C#에서 GUI를 구현하다보면 비즈니스 로직을 스레드로 구현하여 백그라운드에서 작동시키고 GUI는 현재의 상태를 출력하는 식의 구현을할 때가 있다. 스레드에서 GUI 쪽을 제어할 때 Cross-Thread로 인한 예외를 방지하기 위해서 SyncrhonizationContext를 사용하게 된다.
SyncrhonizationContext는 Send와 Post 메소드를 제공하는데 잘못 사용하면 GUI에 Deadlock이 발생할 수 있어서 사용상 주의가 필요하다.
SyncrhonizationContext의 Send와 Post 메소드의 차이는 해당 메소드가 리턴되었을 때 입력된 델리게이트의 수행 완료 여부이다. Send는 입력된 델리게이트가 모두 수행된 뒤 리턴되지만 Post는 델리게이트의 수행여부와 상관없이 리턴된다. 그런데 이는 SyncrhonizationContext와 Send나 Post를 호출한 Context간의 동기/비동기 수행 여부를 의미하는 것일 뿐 Send와 Post에 입력된 델리게이트 간의 동기/비동기 수행 여부를 의미하지 않는다. 일례로 호출된 Post의 델리게이트에서 Sleep이나 lock에 의해서 블록되어 있으면 비슷한 시간에 호출된 Send의 델리게이트는 계속 Post의 델리게이트가 종료될 때까지 기다리게 되므로 Deadlock이 발생할 수 있게된다.
Deadlock 예제:
void func1()
{
lock(x)
{
SynchronizationContext.Send ( // <-- deadlock
() =>
{
// ...
}
, null);
}
}
void func2()
{
SynchronizationContext.Post (
() =>
{
lock(x) // <-- deadlock
{
// ...
}
}
, null);
}
// func1, func2가 동시에 실행되었을 때 func1이 func2보다 약간이라도 빨리 수행되면 deadlock 발생 (func2가 약간이라도 먼저 수행되면(lock(x)에 먼저 도달하면) 문제 없음)
// 이 문제를 해결하려면 func2의 lock을 Post 메소드 바깥으로 빼거나 func1의 Send를 Post로 변경해야 함
따라서 SyncrhonizationContext의 Send와 Post는 lock으로 보호된 코드 처럼 한번에 한개씩만 호출됨을 명심하고 입력된 델리게이트 내에서 lock과 같은 동기화 관련 구문을 사용할 때에는 Deadlock을 유발하지 않을지 꼼꼼히 살펴보아야 한다.
댓글 없음:
댓글 쓰기