2012-01-21 5 views
4

리눅스에서 작성한 코드 조각에서 이상한 동작을 보았습니다. 누군가 그 원인을 알고 있는지 공유하고 싶습니다. 기본 클래스와 파생 클래스가 있습니다. 기본 클래스에서 가상 메서드를 정의했으며 파생 클래스에서 동일한 서명으로 해당 메서드를 다시 정의했습니다. 그런 다음 boost를 사용하여 스레드를 시작합니다.가상 함수 및 부스트 바인딩 이상한 동작

기본 클래스의 해봐요 방법은 그것을 할 예정이었다 무슨 짓을했는지
boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); 

반면, 같은 방법 : 내가 이런 짓을 파생 클래스의 초기화 방법에

Class Base { 
public: 
    virtual void DoSomething(); 
    virtual void Init() = 0; 
    ... 
} 

Class Derived : public Base { 
public: 
    void DoSomething(); 
    void Init(); 
    ... 
} 

: 다음은 샘플 코드는 파생 된 클래스의 메서드는 비어있는 메서드였으며 실수로 거기에 남아있었습니다. 위의 코드를 실행하는 동안, 대부분의 경우 Base 클래스의 DoSomething이 스레드에서 실행되었으므로 응용 프로그램이 제대로 작동하지만 때로는 작동하지 않는 경우가있었습니다. 일부 디버깅 후에 위의 실수를 지적하고 파생 클래스의 DoSomething을 제거하면 문제가 해결됩니다. 디버그 모드에서 이클립스를 사용하면 파생 클래스의 DoSomething 메서드가 항상 호출되는 것처럼 보이지만 콘솔에서 응용 프로그램을 실행하는 것은 대부분의 경우에 작동하지만 항상 그런 것은 아닙니다. 이 행동에 대한 이유가 있습니까? 왜 때로는 바인드 함수가 기본 클래스 메서드를 사용하고 때로는 파생 클래스의 동일한 메서드를 사용하는 것입니까? 사전에

덕분에 응답

편집

전체 작업 예제를 보여 어려울 것이다, 내가 클래스를 사용하는 방법을 조금 보여주기 위해 노력할 것입니다 @pmr합니다.

먼저 파생 된 개체를 인스턴스화 한 다음 init 함수에서 위에 표시된 초기화 코드로 스레드를 시작합니다. DoSomething에는 벡터에서 반복되는 while 루프가 있지만 생각만큼이나 중요하지 않습니다.

void Derived::Init() 
{ 
    ... 
    boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); 
} 

void Base::DoSomething() 
{ 
    while(true) { 
     ... 
    } 
} 

void Derived::DoSomething() 
{ 
} 

당신이 그렇게 가끔 대신 기본 해봐요 기능에서 일어났다 어떤 처리를 보지 못했다, 파생 해봐요 방법은 비어있는이 코드에서 볼 수 있듯이.

+0

최소한의 편집 가능한 예를 제공하는 데주의해야합니까? 또는 최소한 클래스 사용 방법을 보여줍니다. – pmr

답변

2

이 동작의 이유를 알아 냈습니다. 처음에는 Base 클래스 생성자 내에서 스레드 생성자를 호출했습니다. 이것은 기본 생성자가 파생 된 것보다 먼저 호출 되었기 때문에 vtable이 빈 파생 함수를 가리 키도록 만들어 졌기 때문에 때때로 vtable이 작성되기 전에 스레드가 시작 되었기 때문에 바인드 함수가 기본 메소드를 사용했기 때문에 이것이 문제라고 생각합니다. , 그것은 그것이 의미했던 것을했다.디버그를 사용하여 일부 지연이 발생했다는 것을 알았습니다. 따라서 디버거를 사용하면 스레드가 항상 파생 클래스 메서드에 바인딩되어 잘못된 동작이 발생합니다. 또한, init 함수 내에서 스레드 생성을 이동 시키려고 시도했습니다. 그런 식으로 파생 된 함수가 항상 호출됩니다.

4

다음은 야생 추측입니다. 실제로 스레드를 시작하는 데 사용한 객체가 파괴되었습니다! 가상 함수의 바인딩은 파기하는 동안 변경되므로 (객체가 파기되면 모든 가상 함수는 사용 된 객체가 현재 파기되는 클래스의 유형 인 것처럼 해결됩니다). 이를 위해 "vtable 포인터"는 일반적으로 적절한 "가상 함수 테이블"을 가리 키도록 재설정됩니다. 기지가 파괴되면 더 이상 대상을 파괴 할 필요가 없습니다.

이것은 임의적 인 동작에 대한 설명과 잘 어울립니다. 때때로 부모 스레드가 기본 클래스 생성자에 도달 할만큼 빠르게 실행되고 때로는 그렇지 않은 경우가 있습니다. 디버그 모드로 컴파일 할 때, 부모 스레드는 객체를 파기하기 전에 일관되게 오래 걸렸습니다. 많은 경우에 모든 것이 정상적으로 작동했다는 진술은 그 이미지를 실제로 파괴하지 않습니다. 종종 버그가있는 코드는 작동하는 것처럼 보이지만 더 면밀히 검사 할 때 erradic 동작을 보여줍니다.

+0

안녕하세요, 클래스가 파괴되지 않기 때문에이 경우라고 생각하지 않습니다. 스레드 초기화 후 같은 클래스의 다른 함수를 사용합니다 (이것은 일종의 데몬이므로 응용 프로그램이 오랫동안 실행됩니다) . – cpl

0

우리는 우리의 내부 OS에서 동일한 문제에 직면했다. 우리는 객체가 생성 될 때 일부 가상 함수를 다른 작업자 스레드에 바인딩한다. OS가 클래스의 생성 함수를 파생시키기 전에 작업자 스레드로 전환하면 worker는 "this"를 기본 클래스 유형으로 호출합니다. 그래서, 우리는 그것을 기술 할 수 있다고 생각합니다 : "this"포인터는 생성자와 생성자에서 쓰레드 안전하지 않습니다.