멀티스레드 프로그래밍 할 때 기억해야할 것

멀티스레드 프로그래밍을 하다보면 다음 두 가지가 잘 구분 되어야하고, 이 둘이 모두 보장되야 한다.

  • 객체의 생명 주기
  • 객체의 일관성 유지 (동기화 문제)

구체적으로 말하면 이런 차이가 있다.

객체의 생명 주기라는 것은, 특정 객체가 _살아있고, 접근해서 쓸 수 있는 상태_인 시간에 대한 표현이다. 예를 들어서 서버 응용을 만드는데, 클라이언트에 연결된 소켓의 생명 주기란 것은 “클라이언트와 연결되고 ~ 클라이언트와 연결이 종료되고” 라는 두 가지 이벤트 사이의 시간이 된다. 그리고 이 주기는 의미상으로 그리고 프로그램적으로 안전하고 유효한 접근인가 에 관한 것을 결정하게 된다. 그리고 이 생명 주기를 어느 한 스레드가 종료시켜버리면 다른 스레드들은 “그 사실을 알 수 없더라도”, 그 이후의 해당 객체에 대한 접근은 모두 “안전하지 못하고 유효하지 않다”.

객체의 일관성은 “살아있는 객체“에 접근을 하고, 여러 스레드에서 동시에 접근해서 “변경할 때“의 안정성에 관한 것이다. 흔히 멀티스레드 프로그래밍에서 말하는 “동기화"라는 것은 이런 쪽에 해당하는 내용들이다.

즉, 멀티스레드 프로그램을 한다는 것은, 적어도 이 두 가지 사항을 잘 알고 진행하는게 되야 한다. 만약 생명 주기만 지켜지면 업데이트가 동시에 일어나서 이상한 객체 상태로 갈 수 있다. 반대로 동기화는 되지만 생명 주기를 보장하지 않으면 잘해야 이상한 메모리에 쓰거나, 보통은 segmentation fault / access violation 으로 끝난다. 물론(?) 최악의 경우 $SP/$FP를 덮어 쓴다거나 이런 저런 오버플로우에러로 거의 잡을 수 없는 버그를 만들기도 한다.

그래서 특정 공유 객체를 접근할 때는,

객체가 살아있어야 하고 (생명 주기),

일관성이 유지되야한다 (동기화 문제)

라는 것이다.

투표하고 무거운 발걸음으로 출근해서, 지난 주에 완성하지 못한 코드를 고치는데 생명 주기는 보장되는데1, 동기화 된 업데이트를 고려 안한 코드 무더기를 발견하고 괴로워하고 있다. 덤으로 더 큰 문제는 개별 객체에 락이 없고, 적절하게 락을 소유할 주체도 없기 떄문에 … Orz


  1. boost::shared_ptr 를 적절히 써서 해당 코드에서의 모든 접근은 객체가 살아있을 때만 동작한다는게 보장되어있다. ↩︎