Inter-thread communication in C++ #8

Posted at 2008. 11. 6. 00:11 // in S/W개발/C++ 이야기 // by 김윤수


설계 결함이 또 발견돼서 수정했습니다. 거의 매일 같이 major change가 발생하네요. 이게 다 C++ class library 설계 경험이 부족한 탓인 것 같습니다.

이번에 바뀐 내용은 다음과 같습니다.

  • ITCThread의 세가지 사용 모드 즉, 상속 모드, 기존 클래스 조합 모드, 일반 함수 호출 모드 이렇게 세가지를 ITCThread 만 가지고 처리하던 걸 이제는 ITCThread, ITCAdaptor, FunctionITCAdaptor로 분리해서(ITCAdaptor를 없애 버렸었고, FunctionITCAdaptor는 ITCThread에서 상속받고 있었지요) 지원하고, 이 세 클래스의 Base 클래스로  ITCThreadBase를 만들었습니다. ITCThreadBase를 만든 이유는 템플릿으로 인한 code bloat를 방지하기 위함입니다.
  •  그전에는 세 가지 모드를 지원하기 위한 멤버 함수를 ITCThread에 모두 정의해 놓고 있었는데, 이제는 각 세가지 모드를 담당하는 클래스에 골고루 분배해 놓았습니다. 이렇게 하면서 그전에 있던 심각한 설계 결함이 해결됐습니다. ITCThread안에서 Callee 객체를 release 하지 못하는 결함이었습니다. 실은 이 문제는 ITCThread와 ITCAdaptor를 하나로 합쳐 놓았기 때문에 발생하는 문제이기도 했습니다. ITCThread는 상속을 통해서 새로운 ITCThread의 파생 클래스를 정의하기 위한 클래스인데, 이 경우 Callee는 파생 클래스 인스턴스가 되어버리고 기본 클래스의 dtor에서 파생 클래스의 dtor를 호출할 수는 없는 일이기 때문입니다. 그래서 기존에는 기존 클래스 조합 모드(지금은 ITCAdaptor를 사용합니다)에서 Callee 객체를 넘겨주고 나서 그 객체의 ownership을  ITCThread에게 주지 못하고, Caller쪽에서 직접 삭제해야 하는 문제가 있었습니다. 기존에는 다음과 같이 했었는데,
  •     s = "hello";
        Hello* h = new Hello();
        HelloITCAdaptor hello(h, s);
        for (int i = 0; i < 1000; ++i)
        {
            hello.sayHelloTo("Yoonsoo Kim");
        }
        hello.quit();
        hello.join();
        delete h;

  • 이제는 다음과 같이 하면 됩니다.
  •     // example #3
        s = "hello";
        HelloITCAdaptor hello(shared_ptr<Hello>(new Hello()), s);
        for (int i = 0; i < 1000; ++i)
        {
            hello.sayHelloTo("Yoonsoo Kim");
        }
        hello.quit();
        hello.join();
  • ITCThreadBase::queue(CallType call) 이 추가되었습니다. 원한다면 사용자가 ITCThreadBase::queue()와 bind를 적절히 이용해서 어떤 멤버 함수던 그냥 함수던 호출할 수가 있습니다. DoIt() 보다는 low-level 이기 때문에 상당히 사용하기가 불편합니다.
  • 이 외에 이런 저런 code cleanup을 했습니다.
주의할 점이 하나 있다면 ITCAdaptor의 경우, Callee 로 넘겨주는 객체에 대한 ownership이 ITCAdaptor에게 넘어가기 때문에 Caller쪽에서는 사용하면 안된다는 것입니다. 그래서 아래와 같이 프로그래밍 하는게 좋은 practice입니다.

    s = "btn_handler";
    ButtonHandlerITCAdaptor btnHdlr(
        shared_ptr<ButtonHandler>(new ButtonHandler()), s);

    btnHdlr.doIt(&ButtonHandler::onClick, 10, 20);
    btnHdlr.quit();
    btnHdlr.join();

즉, Caller쪽에서는 ButtonHandler 객체를 가리키는 포인터를 유지하지 않는 것이죠.

소스 코드 첨부하니 참고하시기 바랍니다.