1

私が行ったいくつかの職場で見た設計上の問題ですが、満足のいく解決策はありません。

動的な数のスレッドを持つシステムがあるとします。

各スレッドは一連の「シングルトン」にアクセスできる必要があります。シングルトンはスレッドごとに 1 つのインスタンスしか持ちません (したがって、それらは実際のシングルトンではなく、スレッドごとのシングルトンです)。

このシングルトンのセットは、コンパイル時に認識されます。

各シングルトンにはデフォルトのコンストラクターがあります (物事を単純化するために、この制約を持たないソリューションが優れています)

満足のいく解決策には、次のものが必要です。

  1. 各スレッドは o(1) 時間でそのシングルトンのいずれかにアクセスできます

  2. シングルトンへのアクセスはロックフリーです

  3. 「シングルトン セット」にシングルトンを追加しても、セット側で新しいコードを記述する必要はありません

  4. 「シングルトン セット」はコンパイル時に設定されます

そのような設計が実現可能かどうかはわかりません。もしそうなら、メタプログラミングが少し必要だと思います。

ご意見をお寄せいただきありがとうございます。

4

4 に答える 4

1

私があなたを誤解していない限り、あなたはスレッドローカルストレージについて説明しています。

thread_localC++11 では、スレッドごとに個別のインスタンスを取得するために変数を宣言するだけです。

C++03 では、最も移植性の高いソリューションは次のとおりですboost::thread_specific_ptr。あるいは、コンパイラやシステム ライブラリが、POSIXpthread_key_createやその仲間などのスレッド固有のストレージを提供する場合があります。

于 2012-07-19T08:54:35.070 に答える
1

スレッドローカル変数は問題をうまく解決します。

// in .h
class ThreadSingleton
{
private:
    static __thread ThreadSingleton* thread_specific_instance;

public:
    static ThreadSingleton* get() { return thread_specific_instance; }
    ThreadSingleton();
    ~ThreadSingleton();
};

// in .cc
__thread ThreadSingleton* ThreadSingleton::thread_specific_instance;

ThreadSingleton::ThreadSingleton() {
    if(thread_specific_instance)
        std::abort(); // one instance per thread please
    thread_specific_instance = this;
}

ThreadSingleton::~ThreadSingleton() {
    thread_specific_instance = 0;
}

// usage
int main() {
    // on thread entry
    ThreadSingleton x;

    // later anywhere in the thread
    ThreadSingleton* px = ThreadSingleton::get();
}

各スレッドはThreadSingleton、通常はスレッド関数内のスタック上のどこかに作成します。後で、呼び出し元のスレッドのシングルトンを返すことによりThreadSingleton、そのスレッドのどこからでもアクセスできます。ThreadSingleton::get()(上記は他のクラスをラップするテンプレートにすることができますが、説明を簡単にするためにそれを行いませんでした)。

スレッド ローカル変数へのパフォーマンス上のアクセスには、呼び出しは必要ありません ( を使用して作成されたスレッド固有のストレージを使用する場合とは異なりますpthread_key_create) 。詳細については、 http://www.akkadia.org/drepper/tls.pdfを参照してください。

于 2012-07-19T08:29:02.847 に答える
0

わかりました、通常、これをコメントに投稿します。あなたの質問を正しく理解しているかどうかさえよくわからないからです...しかし、親 Thread クラスのコンストラクターでシングルトン セットのインスタンスを作成するだけでは十分ではありませんか? ?

A、B、C の 3 つのクラス (コンパイル時に既知) と 1 つの「スレッド」クラスがあるとします。

スレッド ヘッダーで A、B、および C のインスタンスを宣言しませんか

class Thread {
private:
    A *a;
    B *b;
    C *c;
public:
    Thread();
}

次に、スレッドのコンストラクターでそれらをインスタンス化しますか?

Thread:Thread() {
    a = new A();
    b = new B();
    c = new C();
}

このように、各スレッドはシングルトンを排他的に「所有」します。つまり、競合状態やロックを心配することなく、いつでもシングルトンにアクセスできます。

「シングルトンの追加」に関しては、「Singleton-Parent」クラスを作成してから、新しいポインタをプッシュできる標準コンテナ (std::list など) を使用することをお勧めしますか? もちろん、このリストへのアクセスはロックで保護する必要があります。実行時やコンパイル時にアクセスする場合、これは必要ありません。コンパイル時には、ポインターの静的配列を使用することをお勧めします。これにより、ポインターにできるだけ速くアクセスできます。

繰り返しますが、あなたの質問を間違って理解していたら申し訳ありません。

于 2012-07-19T08:12:59.987 に答える
-1

あなたの質問を正しく理解しているかどうかはわかりませんが、私にとってシングルトンの「セット」は無関係です。固定数のシングルトンがあり、それらSingleton1をに呼び出します。SingletonXここで、Xはコンパイル時に認識されます。スレッドにはあまり関係ありません。

実際のシングルトンについては、スレッドごとの部分を処理する単一のテンプレート化された基本クラスから継承させることができます。このようなもの:

template<class B>
struct SingletonBase
{
    static B &getInstance()
        {
            // One instance per thread
            static std::unordered_map<std::thread::id, B> intances;

            if (instances.find(std::this_thread::get_id()) == instances.end())
                instances[std::this_thread::get_id()] = B{};

            return instances[std::this_thread::get_id()];
        }
};

class Singleton1 : public SingletonBase<Singleton1>
{
    // ...
};

シングルトンに個別の個別のクラスが必要ない場合は、std::arrayそれらを保存するために使用できます。

class Singleton : public SingletonBase<Singleton>
{
    // ...
};

std::array<Singleton, X> singletons;

Xこれにより、コンパイル時に指定された数の配列が作成され、通常の配列のようにアクセスできます: Singleton &instance = singletons[0].getInstance();.

サンプル コードでは、「新しい」C++11 標準ライブラリの機能を使用していることに注意してください。

于 2012-07-19T08:31:52.420 に答える