2010-07-29 2 views
1

두 개의 서로 다른 스레드에서 호출되는 객체가 있는데 두 객체 모두에서 호출 한 후 "delete this"로 자체를 파괴합니다.자체를 삭제하는 객체의 스레드 안전 구현

이 스레드 안전성을 구현하려면 어떻게해야합니까? thread-safe는 객체가 결코 정확히 한 번 파괴되지 않는다는 것을 의미합니다 (두 번째 콜백 이후 자체를 파괴해야 함). 이 또한 뮤텍스를 삭제 때문에,

class IThreadCallBack                        
{                             
    virtual void CallBack(int) = 0;                     
};                             

class M: public IThreadCallBack                     
{                             
private:                           
    bool t1_finished, t2_finished;                     

public:                           

    M(): t1_finished(false), t2_finished(false)                  
    {                            
    startMyThread(this, 1);                      
    startMyThread(this, 2);                      
    }                            

    void CallBack(int id)                       
    {                            
    if (id == 1)                         
    {                            
     t1_finished = true;                       
    }                            
    else                           
    {                            
     t2_finished = true;                       
    }                            

    if (t1_finished && t2_finished)                    
    {                            
     delete this;                         
    }                            
    }                            
};                             

int main(int argc, char **argv) {                     
    M* MObj = new M();                        
    while(true);                          
}                             

은 분명히 내가 개체의 구성원으로 뮤텍스를 사용하고 삭제를 잠글 수 :

나는 몇 가지 예제 코드를 만들었습니다. 반면, finested-flag가 설정된 뮤텍스 보호 영역 안에 "toBeDeleted"플래그를 설정하면 오브젝트가 전혀 삭제되지 않는 상황이 있는지 확실하지 않습니다. 스레드 구현은 어떤 경우에도 스레드 당 정확히 한 번 콜백 메소드가 호출되도록합니다.

편집/업데이트 :

void CallBack(int id) 

{  
    mMutex.Obtain() 
    if (id == 1) 
    { 
    t1_finished = true; 
    } 
    else 
    { 
    t2_finished = true; 
    } 
    bool both_finished = (t1_finished && t2_finished); 

    mMutex.Release(); 

    if (both_finished) 
    { 
    delete this;  
    } 
} 

이가 안전한 것으로 간주 수 : 나는 콜백 (..)를 변경하면 어떻게 ? (mMutex가 m 클래스의 멤버입니까?)

뮤텍스를 공개 한 후에도 아무 멤버에게도 액세스하지 않으면 내가 생각하는 것처럼?!

+0

자동 삭제 하시겠습니까? 왜이 객체는 그것을 사용하는 스레드에 의해 파괴되지 않을까요? –

+2

개체를 삭제하는 것은 나쁜 생각입니다. –

+0

이유는 두 가지입니다. 왜 스레드를 사용하여 스레드를 삭제할 수 없습니까? 1. 두 스레드는 서로를 알 수 없습니다. 2. 스레드가 외부 라이브러리에 속합니다. – IanH

답변

3

Boost's Smart Pointer을 사용하십시오. 이것을 자동으로 처리합니다. 당신의 객체는 자신을 삭제할 필요가 없으며 스레드로부터 안전합니다.

편집 : 위 게시 한 코드에서
, 정말 말할 수 없다, 더 많은 정보가 필요합니다. 하지만 이렇게 할 수 있습니다 : 각 쓰레드는 shared_ptr 객체를 가지고 있고 콜백이 호출되면 shared_ptr :: reset()을 호출합니다. 마지막 재설정은 M을 삭제합니다. 각 shared_ptr은 각 스레드에 thread local storeage과 함께 저장 될 수 있습니다. 따라서 본질적으로 각 스레드는 자체 shared_ptr을 담당합니다.

+2

이 옵션을 사용할 수 있다면 내가 생각할 수있는 것보다 확실히 좋습니다. –

+0

누가 스마트 포인터를 담당합니까? 콜백은 외부 라이브러리에서 이루어집니다. – IanH

+0

@IanH 자세한 내용을 추가했습니다. – Gianni

3

두 개의 별도 플래그를 사용하는 대신 대기중인 스레드 수를 카운터로 설정 한 다음 인터록 감소를 사용하여 카운터를 설정할 수 있습니다.

그러면 스레드 카운터가 0에 도달하면 작업이 완료되고 정리해야한다는 것을 100 % 확신 할 수 있습니다.

인터록 감소에 대한 자세한 내용은 on Windows, on Linuxon Mac입니다.

+0

OK, 나는 고쳐 쓴다. 있을 수있다. 다행이 아닌 것은 나다. –

+0

그래, 내 예제가 너무 단순화 된 것 같아. 실제로는 추가 콜백 매개 변수 (성공 또는 실패)가 있고 첫 번째 스레드가 성공하면 차이가 발생하지만 두 번째 것은 중요하지 않습니다 (단지 successl 또는 unsuccessfull 콜백을 기다려야 함). 또한 성공 (또는 실패)에 대해 정확히 한 번만 다른 구성 요소에 알려야합니다 (비동기 메시지 메커니즘을 통해). 예제 코드를 곧 업데이트 할 예정입니다. – IanH

1

더 강력한 구현은 참조 카운팅을 구현하는 것입니다. 시작하는 각 스레드에 대해 카운터를 늘립니다. 각 콜백 호출에 대해 카운터를 줄이고 카운터가 0에 도달하면 객체를 삭제하십시오. lock 카운터 액세스를 할 수도 있고 Interlocked 클래스를 사용하여 카운터 액세스를 보호 할 수도 있지만이 경우 첫 번째 스레드 완료와 두 번째 시작 간의 잠재적 인 경쟁에주의해야합니다.

업데이트 : 물론 나는 이것이 C++이라는 사실을 완전히 무시했습니다. :-) InterlockExchange을 사용하여 C# Interlocked 클래스 대신 카운터를 업데이트해야합니다.

2

나는 다음과 같은 방법으로 조작하여 완전히 delete this의 ickiness과 혼란을 피할 그 같은 한 번 구현 뭔가 :

  • 공유 객체의 이러한 종류의 삭제에 대한 책임이 스레드를 시작에 대기 조건
  • 공유 객체가 더 이상 사용되지 않을 때 자체를 삭제하는 대신 스레드 안전 대기열에 자신을 삽입하고 삭제 스레드가 대기중인 상태를 알립니다.
  • deleter 스레드가 깨어날 때 , 큐에있는 모든 것을 삭제합니다.

프로그램에 이벤트 루프가있는 경우 "사용하지 않은 공유 개체 삭제"를 의미하는 이벤트 유형을 만들어 동일한 영구 객체가이 이벤트에 응답하게하려면 별도의 스레드 생성을 피할 수 있습니다 위의 예제에서 deleter 쓰레드가 될 것입니다.

2

특히 클래스 자체 내에서 이것이 가능할 것이라고 상상할 수 없습니다. 문제는 두 가지입니다 :

1) 포인터를 삭제 한 경우 "콜백"을 호출 한 후 바깥 세상이 포인터를 0으로 설정해야하는 이유를 외부 세계에 알릴 방법이 없습니다.

2) 일단 두 개의 스레드가이 기능을 사용하면 내 프랑스어를 용서하고 절대적으로 망할 것입니다. 삭제 된 객체에서 함수를 호출하는 것은 UB입니다. 누군가가 그 객체에있는 동안 객체를 삭제하는 것이 무엇인지 상상해보십시오.

나는 결코 이것을 "삭제"를 혐오스러운 것으로 보지 못했습니다. 아주 희소 한 조건에서 때때로 필요하지 않음을 의미하지는 않습니다. 문제는 사람들이 그것을 너무 많이하고 그러한 디자인의 결과에 대해 생각하지 않는다는 것입니다.

나는 "삭제 될 것"이 잘 작동 할 것이라고 생각하지 않습니다. 두 개의 스레드에서 작동하지만 세 개는 무엇입니까? 보호를 삭제할 때 (명시된대로) 및 필연적으로 발생할 UB 때문에 삭제를 호출하는 코드 부분을 보호 할 수 없습니다. 그래서 첫 번째는 통과하고, 깃발을 세우고 중단합니다 .... 나머지 중 어느 것이 출입구에서 삭제를 호출 할 것입니까?

+0

1)은 각 스레드가 한 번만 콜백한다는 것을 절대적으로 확신하기 때문에 충족됩니다. 아무도 객체와 객체에 대한 포인터를 알지 못합니다. 2) 정확히 내 문제입니다. 외부 라이브러리가 하나의 이벤트 루프를 사용하여 두 콜백이 같은 스레드에 도착할 것이라고 생각했지만 오늘은 그렇지 않습니다. "이 항목 삭제"관련 : 누가 개체를 삭제해야합니까? 콜백을 수행하는 외부 라이브러리는 변경할 수 없습니다. 그리고 만약 "toBeDeleted"플래그를 설정하는 것이 같은 방식으로 잘못 될 수 있기 때문에 나는 나의 객체를 관리하기 위해 관리자 클래스를 사용하면 스레드 문제를 해결할 수 없다. – IanH