문제

참조하는 스레드 수를 추적하고 싶은 객체가 있습니다. 일반적으로 객체의 모든 메소드가 호출되면 나사산 로컬 부울 값을 확인하여 현재 스레드에 대한 카운트가 업데이트되었는지 여부를 결정할 수 있습니다. 그러나 사용자가 말하는 경우 도움이되지 않습니다. 부스트 :: 바인드 바인드 내 객체를 Boost :: 함수에 바인딩하고 Boost :: Thread를 시작합니다. 새 스레드는 내 객체에 대한 참조를 가지며, 그 방법을 호출하기 전에 무기한 기간 동안 유지할 수 있으므로 오래된 수를 초래할 수 있습니다. 나는 이것을 처리하기 위해 boost :: 스레드 주위에 내 래퍼를 쓸 수 있지만, 사용자 boost :: bind 's a object는 도움이되지 않습니다. 그것은 포함되어 있습니다 내 객체 (멤버 유형의 존재를 기반으로 전문화 할 수 없습니다. 적어도 그 방법을 모릅니다).

이것을 할 방법이 있습니까? 내가 생각할 수있는 유일한 방법은 사용자로부터 너무 많은 작업이 필요하다는 것을 의미합니다. rapper 주변의 래퍼를 제공합니다 :: 존재하는 경우 전달되는 객체에서 특수 후크 메소드를 호출하는 스레드를 제공하고 사용자는 모든 클래스에 특수 후크 메소드를 추가합니다. 내 개체가 포함되어 있습니다.

편집 :이 질문을 위해 새로운 스레드를 만들기위한 수단을 제어한다고 가정 할 수 있습니다. 예를 들어 Boost :: Thread를 랩핑 할 수 있으며 사용자가 내 랩핑 된 버전을 사용하고 PTHREADS 등을 사용하여 사용자에 대해 동시에 걱정할 필요가 없습니다.

edit2 : __thread 또는 boost::thread_specific_ptr. 그것은 현재 표준에 있지 않지만 곧 곧있을 것입니다.

도움이 되었습니까?

해결책

일반적으로 이것은 어렵습니다. "누가 나에 대한 언급이 있습니까?" 일반적으로 C ++에서는 해결할 수 없습니다. 해결하려는 특정 문제의 더 큰 그림을 살펴보고 더 나은 방법이 있는지 확인할 가치가 있습니다.

내가 당신을 그곳으로 데려 갈 수있는 몇 가지가 있지만, 그들 중 어느 것도 당신이 원하는 것을 가지고 있지 않습니다.

객체에 대한 "소유 스레드"의 개념을 설정하고 다른 스레드 인 LA QT GUI 요소의 작업을 거부 할 수 있습니다. (소유자가 아닌 다른 스레드에서 스레드에서 스레드를 사용하려고 시도하는 것은 실제로 스레드 안전성을 제공하지 않습니다. 소유자가 확인되지 않으면 다른 스레드와 충돌 할 수 있기 때문입니다.) 이렇게하면 최소한 사용자가 실패합니다. 빠른 행동.

사용자가 가시 가능한 객체를 구현 오브젝트 자체에 대한 가벼운 참조 인 경우 (이를 문서화함으로써) 참조 계산을 장려 할 수 있습니다. 그러나 결정된 사용자는이 문제를 해결할 수 있습니다.

그리고이 두 가지를 결합 할 수 있습니다. 즉, 각각에 대한 스레드 소유권 개념을 가질 수 있습니다. 참조, 그런 다음 물체가 누가 참조를 소유 한 사람을 알게되도록하십시오. 이것은 매우 강력 할 수 있지만 실제로 바보를 방지 할 수는 없습니다.

사용자가 객체로 할 수있는 것과 할 수없는 것을 제한하기 시작할 수 있지만 의도하지 않은 오류의 명백한 소스보다 더 많은 것을 다루는 것이 가치가 있다고 생각하지 않습니다. 사람들이 당신의 물건에 포인터를 가져갈 수 없으므로 운영자와 개인을 선언해야합니까? 사람들이 당신의 대상을 동적으로 할당하는 것을 막아야합니까? 그것은 어느 정도 사용자에 따라 다르지만, 당신은 예방할 수 없다는 것을 명심하십시오. 참조 결국 whack-a-mole을 연주하면 당신을 미치게 할 것입니다.

따라서 원래의 제안으로 돌아가십시오. 가능하면 큰 그림을 다시 분석하십시오.

다른 팁

모든 단절 전에 threadid 확인을 수행하는 PIMPL 스타일 구현이 부족합니다.

 class MyClass;
 class MyClassImpl {
     friend class MyClass;
     threadid_t owning_thread;
 public:
     void doSomethingThreadSafe();
     void doSomethingNoSafetyCheck();
 };

 class MyClass {
     MyClassImpl* impl;
 public:
     void doSomethine() {
         if (__threadid() != impl->owning_thread) {
             impl->doSomethingThreadSafe();
         } else {
             impl->doSomethingNoSafetyCheck();
         }
     }
 };

참고 : OP가 활성 포인터가있는 스레드를 나열하고 싶다는 것을 알고 있습니다. 위의 구현은 최소한 논쟁이있을 수있는시기를 알 수 있습니다. randing_thread를 변경할 때 Dosomething이하는 일에 크게 의존합니다.

보통 당신은 이것을 프로그래밍 방식으로 할 수 없습니다.

불행히도,가는 길은 특정 객체가 공유되고 다른 사람들은 사본이라는 것을 증명할 수있는 방식으로 프로그램을 설계하는 것입니다.

현재 C ++ 표준에는 스레드 개념조차 없으므로 특히 스레드 로컬 스토리지의 표준 휴대용 개념이 없습니다.

귀하의 문제를 올바르게 이해하면 Win32 기능을 사용하여 Windows에서 수행 할 수 있다고 생각합니다. getCurrentThreadId (). 아래는 사용 방법에 대한 빠르고 더러운 예입니다. 스레드 동기화는 잠금 객체와 함께 수행해야합니다.

객체의 모든 멤버 함수 상단에 CMYTHREADTRACKER의 객체를 작성하는 경우 스레드 용으로 추적합니다. _handle_vector 객체를 사용하는 스레드 ID가 포함되어야합니다.

#include <process.h>
#include <windows.h>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;


class CMyThreadTracker
{

    vector<DWORD> & _handle_vector;
    DWORD _h;
    CRITICAL_SECTION &_CriticalSection;
public:
    CMyThreadTracker(vector<DWORD> & handle_vector,CRITICAL_SECTION &crit):_handle_vector(handle_vector),_CriticalSection(crit)
    {
        EnterCriticalSection(&_CriticalSection); 
        _h = GetCurrentThreadId();
        _handle_vector.push_back(_h);
        printf("thread id %08x\n",_h);
        LeaveCriticalSection(&_CriticalSection);
    }

    ~CMyThreadTracker()
    {
        EnterCriticalSection(&_CriticalSection); 
        vector<DWORD>::iterator ee = remove_if(_handle_vector.begin(),_handle_vector.end(),bind2nd(equal_to<DWORD>(), _h));
        _handle_vector.erase(ee,_handle_vector.end());
        LeaveCriticalSection(&_CriticalSection);
    }
};

class CMyObject
{
    vector<DWORD> _handle_vector;

public:
    void method1(CRITICAL_SECTION & CriticalSection)
    {
        CMyThreadTracker tt(_handle_vector,CriticalSection);

        printf("method 1\n");

        EnterCriticalSection(&CriticalSection);
        for(int i=0;i<_handle_vector.size();++i)
        {
            printf(" this object is currently used by thread %08x\n",_handle_vector[i]);
        }
        LeaveCriticalSection(&CriticalSection);

    }
};

CMyObject mo;
CRITICAL_SECTION CriticalSection;

unsigned __stdcall ThreadFunc( void* arg )
{

    unsigned int sleep_time = *(unsigned int*)arg;
    while ( true)
    {
        Sleep(sleep_time);
        mo.method1(CriticalSection);
    }

    _endthreadex( 0 );
    return 0;
} 

int _tmain(int argc, _TCHAR* argv[])
{

    HANDLE hThread;
    unsigned int threadID;

    if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) ) 
        return -1;

    for(int i=0;i<5;++i)
    {
        unsigned int sleep_time = 1000 *(i+1);

        hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunc, &sleep_time, 0, &threadID );
        printf("creating thread %08x\n",threadID);
    }

    WaitForSingleObject( hThread, INFINITE );

    return 0;
}

edit1 : 의견에 언급 된 바와 같이, 참조 분배는 아래와 같이 구현 될 수 있습니다. 벡터는 객체를 참조하는 고유 스레드 ID를 보유 할 수 있습니다. 다른 스레드에 의해 복사되는 객체 참조를 처리하려면 사용자 정의 할당 연산자를 구현해야 할 수도 있습니다.

class MyClass
{
public:
static MyClass & Create()
    {
    static MyClass * p = new MyClass();

    return *p;
    }
    static void Destroy(MyClass * p)
    {
        delete p;
    }

private:
    MyClass(){}
    ~MyClass(){};
};

class MyCreatorClass
{
    MyClass & _my_obj;
public:
MyCreatorClass():_my_obj(MyClass::Create())
    {

    }

    MyClass & GetObject()
    {
        //TODO: 
        // use GetCurrentThreadId to get thread id
        // check if the id is already in the vector
        // add this to a vector

        return _my_obj;
    }

    ~MyCreatorClass()
    {
        MyClass::Destroy(&_my_obj);
    }

}; 

int _tmain(int argc, _TCHAR* argv[])
{
    MyCreatorClass mcc;

    MyClass &o1 = mcc.GetObject();

    MyClass &o2 = mcc.GetObject();

    return 0;
}

내가 익숙한 솔루션은 "올바른 API를 사용 하여이 개체와 상호 작용하지 않으면 모든 베팅이 꺼져 있습니다."

요구 사항을 돌리고 개체를 참조하는 스레드가 가능할 수 있습니다. 객체에서 신호를 구독하십시오. 이는 레이스 조건에 도움이되지 않지만 객체가 자체적으로 언로드 된시기 (예 :)가 스레드가 알 수 있도록합니다.

문제를 해결하려면 "객체가 있고 얼마나 많은 스레드가 액세스하는지 알고 싶다"고 말하면 스레드를 열거 할 수 있습니다. 스레드 로컬 스토리지 로이 문제를 해결할 수 있습니다. 객체에 대한 TLS 지수를 할당하십시오. 스레드 TLS를 객체를 가리 키도록 설정하는 "RegisterThread"라는 개인 메소드를 만드십시오.

포스터의 원래 아이디어의 주요 확장은 모든 방법 호출,이 레지스터 스레드 ()를 호출하십시오. 그런 다음 스레드를 언제 또는 누가 만들 때를 감지 할 필요가 없으며, 실제 액세스마다 (종종 중복적으로) 설정됩니다.

객체에 액세스 한 스레드를 확인하려면 TLS 값을 검사하십시오.

거꾸로 : 간단하고 매우 효율적입니다.

단점 : 게시 된 질문을 해결하지만 열거 할 수없는 여러 객체 나 동적 스레드로 부드럽게 확장되지는 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top