2

オブジェクトを参照しているスレッドの数を追跡したいオブジェクトがあります。一般に、オブジェクトのメソッドが呼び出されると、スレッドローカルブール値をチェックして、現在のスレッドのカウントが更新されているかどうかを判断できます。しかし、ユーザーが、boost::bindを使用してオブジェクトをboost::functionにバインドし、それを使用してboost :: threadを開始すると言った場合、これは役に立ちません。新しいスレッドは私のオブジェクトへの参照を持ち、そのメソッドのいずれかを呼び出す前に無期限にそのオブジェクトを保持する可能性があるため、カウントが古くなります。これを処理するためにboost::threadの周りに独自のラッパーを作成することもできますが、ユーザーのboost::bindが次のオブジェクトを含む場合は役に立ちません。私のオブジェクト(メンバータイプの存在に基づいて専門化することはできません-少なくともそれを行う方法はわかりません)そしてそれを使用してboost::threadを開始します。

これを行う方法はありますか?私が考えることができる唯一の手段は、ユーザーからの多大な作業を必要とします-渡されるオブジェクトに対して特別なフックメソッドを呼び出すboost :: threadのラッパーを提供し、オブジェクトが存在する場合、ユーザーは特別なフックメソッドを任意のクラスに追加しますそれは私のオブジェクトを含んでいます。

編集:この質問のために、私は新しいスレッドを作成する手段を制御していると仮定できます。たとえば、boost :: threadをラップして、ユーザーがラップしたバージョンを使用することを期待できます。ユーザーがpthreadなどを同時に使用することを心配する必要はありません。

Edit2:__threadまたはを介してスレッドローカルストレージを利用できる手段があると想定することもできますboost::thread_specific_ptr。現在の標準には含まれていませんが、間もなくリリースされることを願っています。

4

6 に答える 6

3

一般的に、これは難しいです。「誰が私に言及しているのか」という質問。通常、C++では解決できません。解決しようとしている特定の問題の全体像を見て、より良い方法があるかどうかを確認することは価値があるかもしれません。

私が思いつくことができるいくつかのことがありますが、それらのどれもあなたが望むものではありません。

オブジェクトの「所有スレッド」の概念を確立し、他のスレッド、つまりQtGUI要素からの操作を拒否できます。(所有者以外のスレッドからスレッドセーフにしようとしても、実際にはスレッドセーフにはなりません。所有者がチェックされていない場合、他のスレッドと衝突する可能性があるためです。)これにより、少なくともユーザーは失敗します。速い振る舞い。

ユーザーに表示されるオブジェクトを実装オブジェクト自体への軽量参照にすることで(そしてこれを文書化することで)、参照カウントを促進できます。しかし、決心したユーザーはこれを回避できます。

そして、これら2つを組み合わせることができます。つまり、各参照のスレッド所有権の概念を持ち、オブジェクトに参照の所有者を認識させることができます。これは非常に強力である可能性がありますが、実際にはばかげたことはありません。

ユーザーがオブジェクトでできることとできないことを制限し始めることはできますが、意図しないエラーの明らかな原因以上のものをカバーすることは価値があるとは思いません。オペレーターとプライベートを宣言して、他のユーザーがオブジェクトへのポインターを取得できないようにする必要がありますか?人々があなたのオブジェクトを動的に割り当てるのを防ぐべきですか?それはある程度ユーザーに依存しますが、オブジェクトへの参照を防ぐことはできないことを覚えておいてください。そのため、最終的にモグラたたきをすることはあなたを狂気に駆り立てます。

それで、私の最初の提案に戻ってください:可能であれば全体像を再分析してください。

于 2009-06-26T16:33:23.663 に答える
1

すべての間接参照の前に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がアクティブなポインターを持つスレッドをリストしたいことは知っていますが、それは実現可能ではないと思います。上記の実装は、少なくとも、競合が発生する可能性がある場合にオブジェクトに通知します。owning_threadをいつ変更するかは、doSomethingが何をするかに大きく依存します。

于 2009-06-26T15:28:17.373 に答える
1

通常、これをプログラムで行うことはできません。

残念ながら、その方法は、特定のオブジェクトが共有され、他のオブジェクトがスレッドプライベートであることを証明できる(つまり、自分自身を納得させる)ことができるようにプログラムを設計することです。

現在のC++標準にはスレッドの概念すら含まれていないため、特にスレッドローカルストレージの標準的なポータブル概念はありません。

于 2009-06-26T18:35:39.210 に答える
0

私があなたの問題を正しく理解していれば、これはWindowsでWin32関数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;
}

編集1:コメントで述べたように、参照ディスペンスは以下のように実装できます。ベクトルは、オブジェクトを参照する一意のスレッド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;
}
于 2009-06-26T20:15:42.447 に答える
0

私が精通している解決策は、「このオブジェクトと対話するために正しいAPIを使用しない場合、すべての賭けは無効になる」と述べることです。

要件を変えて、オブジェクトを参照するすべてのスレッドがオブジェクトからのシグナルをサブスクライブできるようにすることができる場合があります。これは競合状態には役立ちませんが、スレッドはオブジェクトがそれ自体をアンロードしたことを知ることができます(たとえば)。

于 2009-06-26T23:54:28.917 に答える
0

「オブジェクトがあり、それにアクセスするスレッドの数を知りたい」という問題を解決し、スレッドを列挙することもできるように、スレッドローカルストレージを使用してこの問題を解決できます。オブジェクトにTLSインデックスを割り当てます。オブジェクトを指すようにスレッドTLSを設定するだけの「registerThread」というプライベートメソッドを作成します。

ポスターの元のアイデアの重要な拡張は、すべてのメソッド呼び出し中に、このregisterThread()を呼び出すことです。そうすれば、いつ、誰がスレッドを作成したかを検出する必要はありません。実際のアクセスのたびに、スレッドは(多くの場合、冗長に)設定されます。

オブジェクトにアクセスしたスレッドを確認するには、それらのTLS値を調べるだけです。

利点:シンプルでかなり効率的です。

欠点:投稿された質問を解決しますが、列挙できない複数のオブジェクトまたは動的スレッドにスムーズに拡張されません。

于 2009-06-30T16:23:43.747 に答える