25

この質問はインタビューで尋ねられました。最初の部分は、シングルトンクラスを作成することでした。

class Singleton
{
    static Singleton *singletonInstance;
    Singleton() {}

  public:
    static Singleton* getSingletonInstance()
    {
        if(singletonInstance == null)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }
};

getSingletonInstance()次に、マルチスレッドの状況でこれを処理する方法を尋ねられました。よくわかりませんでしたが、次のように変更しました。

class Singleton 
{
    static Singleton *singletonInstance;
    Singleton() {}
    static mutex m_;

  public:
    static Singleton* getSingletonInstance()
    {
        m_pend();
        if(singletonInstance == null)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }

    static void releaseSingleton()
    {
        m_post();
    }
};

それから、ミューテックスは必要ですが、ミューテックスの保留と投稿は時間がかかるので効率的ではないと言われました。そして、この状況に対処するためのより良い方法があります。

マルチスレッドの状況でシングルトンクラスを処理するためのより良い、より効率的な方法を知っている人はいますか?

4

5 に答える 5

32

C ++ 11では、以下がスレッドセーフな初期化を実行することが保証されています。

static Singleton* getSingletonInstance()
{
    static Singleton instance;
    return &instance;
}

C ++ 03では、一般的なアプローチはダブルチェックロックを使用することでした。フラグ(またはポインタ自体)をチェックして、オブジェクトが初期化されていない可能性があるかどうかを確認し、初期化されていない可能性がある場合にのみミューテックスをロックします。これには、ポインタ(または関連するブールフラグ)をアトミックに読み取る何らかの非標準的な方法が必要です。多くの実装では、プレーンポインタまたはを誤って使用していboolますが、1つのプロセッサでの変更が他のプロセッサで表示される保証はありません。コードは次のようになりますが、ほぼ間違いなく何か問題があります。

static Singleton* getSingletonInstance()
{
    if (!atomic_read(singletonInstance)) {
        mutex_lock lock(mutex);
        if (!atomic_read(singletonInstance)) {
            atomic_write(singletonInstance, new Singleton);
        }
    }
    return singletonInstance;
}

これを正しく行うのは非常に難しいので、気にしないことをお勧めします。C ++ 11では、何らかの理由で例の動的割り当てを維持したい場合は、標準のアトミックタイプとミューテックスタイプを使用できます。

オブジェクトへの同期アクセスではなく、同期初期化についてのみ話していることに注意してください(これは、アクセサでミューテックスをロックし、後で別の関数を介して解放することでバージョンが提供します)。オブジェクト自体に安全にアクセスするためにロックが必要な場合は、すべてのアクセスでロックを回避することはできません。

于 2012-09-03T13:46:25.157 に答える
15

@piokucが提案したように、ここでonce関数を使用することもできます。C ++ 11をお持ちの場合:

#include <mutex>

static void init_singleton() {
    singletonInstance = new Singleton;
}
static std::once_flag singleton_flag;

Singleton* getSingletonInstance() {
    std::call_once(singleton_flag, init_singleton);
    return singletonInstance;
}

そして、はい、これはnew Singleton例外をスローした場合に賢明に機能します。

于 2012-09-03T15:56:06.773 に答える
3

C ++ 11をsingletonInstance使用している場合は、アトミック変数を作成してから、ダブルチェックロックを使用できます。

if (singletonInstance == NULL) {
    lock the mutex
    if (singletonInstance == NULL) {
        singletonInstance = new Singleton;
    }
    unlock the mutex
}
return singletonInstance;
于 2012-09-03T13:26:53.163 に答える
3

インスタンスではなく、実際にシングルトンをロックする必要があります。インスタンスにロックが必要な場合は、呼び出し元が処理する必要があります(または、インスタンスが公開するインターフェイスの種類によっては、インスタンス自体が処理する必要があります)。

サンプルコードを更新します。

#include <mutex>

class Singleton 
{
    static Singleton *singletonInstance;
    Singleton() {}
    static std::mutex m_;

  public:

    static Singleton* getSingletonInstance()
    {
        std::lock_guard<std::mutex> lock(m_);
        if(singletonInstance == nullptr)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }
}
于 2012-09-03T13:32:41.207 に答える
3

使用できるPOSIXスレッドを使用する場合は、この方法pthread_once_tpthread_key_tミューテックスの使用を完全に回避できます。例えば:

template<class T> class ThreadSingleton : private NonCopyable {
public:
    ThreadSingleton();
    ~ThreadSingleton();

    static T& instance();

private:
    ThreadSingleton( const ThreadSingleton& );
    const ThreadSingleton& operator=( const ThreadSingleton& )

    static pthread_once_t once_;
    static pthread_key_t  key_;

    static void init(void);
    static void cleanUp(void*);
};

そして実装:

template<class T> pthread_once_t ThreadSingleton<T>::once_ = PTHREAD_ONCE_INIT;
template<class T> pthread_key_t ThreadSingleton<T>::key_;

template<class T>  
T& ThreadSingleton<T>::instance()
{
    pthread_once(&once_,init);

    T* value = (T*)pthread_getspecific(key_);
    if(!value)
    {   

        value = new T();
        pthread_setspecific(key_,value);
    }   
    return *value;
}

template<class T> void ThreadSingleton<T>::cleanUp(void* data)
{
    delete (T*)data;
    pthread_setspecific(key_,0);
}

template<class T> void ThreadSingleton<T>::init()
{
    pthread_key_create(&key_,cleanUp);
}
于 2012-09-03T13:47:09.283 に答える