Pergunta

Eu estive lendo sobre padrões únicos thread-safe aqui:

http://en.wikipedia.org/wiki /Singleton_pattern#C.2B.2B_.28using_pthreads.29

E diz na parte inferior que a única maneira segura é usar pthread_once -. Que não está disponível no Windows

É que o única forma de garantir rosca inicialização segura?

Eu li esta discussão no SO:

Tópico construção preguiçoso segura de um singleton em C ++

E parece sugerir um swap de nível OS atômico e comparar função, que eu suponho que no Windows é:

http://msdn.microsoft.com/en-us/library /ms683568.aspx

Isso pode fazer o que eu quero?

Editar:. Gostaria de inicialização preguiçoso e para lá para sempre apenas ser uma instância da classe

Alguém em outro site mencionado usando um mundial dentro de um namespace (e ele descreveu um singleton como um anti-padrão) - como ele pode ser um "anti-padrão"

?

aceitado Resposta:
Eu aceitei resposta de Josh como eu estou usando Visual Studio 2008 - NB: Para os leitores futuros, se você não estiver usando este compilador (ou 2005) - não utilize a resposta aceita !!

Editar: O código funciona bem, exceto a instrução de retorno - eu recebo um erro: erro C2440: 'return': não podem converter de 'volátil Singleton *' para 'Singleton *'. Devo modificar o valor de retorno a ser voláteis Singleton *?

Editar: Aparentemente const_cast <> irá remover o qualificador volátil. Obrigado novamente a Josh.

Foi útil?

Solução

Se você estiver usando o Visual C ++ 2005/2008 você pode usar o dobro verificado padrão de bloqueio, uma vez que " variáveis ??voláteis se comportam como cercas ". Esta é a maneira mais eficiente de implementar um singleton lazy-inicializado.

A partir MSDN Magazine:

Singleton* GetSingleton()
{
    volatile static Singleton* pSingleton = 0;

    if (pSingleton == NULL)
    {
        EnterCriticalSection(&cs);

        if (pSingleton == NULL)
        {
            try
            {
                pSingleton = new Singleton();
            }
            catch (...)
            {
                // Something went wrong.
            }
        }

        LeaveCriticalSection(&cs);
    }

    return const_cast<Singleton*>(pSingleton);
}

Sempre que você precisar acessar o singleton, basta ligar GetSingleton (). A primeira vez que ele é chamado, o ponteiro estático será inicializado. Depois ele é inicializado, a verificação NULL irá impedir bloqueio para apenas ler o ponteiro.

NÃO usar este apenas em qualquer compilador, já que não é portátil. O padrão não faz nenhuma garantia sobre como isso vai funcionar. Visual C ++ 2005 adiciona explicitamente a semântica do volátil para tornar isso possível.

Você terá que declarar e inicializar a seção crítica em outras posições do código. Mas que a inicialização é barato inicialização, tão preguiçoso geralmente não é importante.

Outras dicas

Uma maneira simples de garantia cross-platform fio de inicialização segura de um Singleton é a realizá-la explicitamente (através de uma chamada para uma função membro estático no Singleton) no segmento principal de sua aplicação < strong> antes o seu aplicativo é iniciado quaisquer outros tópicos (ou pelo menos quaisquer outros tópicos que irão acessar o singleton).

Garantir fio acesso seguro ao singleton é então conseguida da maneira usual com mutexes / seções críticas.

preguiçoso inicialização também pode ser alcançado usando um mecanismo semelhante. O problema habitual encontrou com isto é que o mutex obrigados a fornecer thread-segurança é muitas vezes inicializado no próprio singleton que apenas empurra o problema thread-segurança para inicialização da seção mutex / crítica. Uma maneira de superar esse problema é para criar e inicializar uma seção mutex / crítico no segmento principal de sua aplicação, em seguida, passá-lo para o singleton através de uma chamada para uma função de membro estático. A inicialização pesado do Singleton pode então ocorrer de uma maneira isenta de segmentos utilizando esta secção exclusão mútua / crítico pré-inicializado. Por exemplo:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};

No entanto, há boas razões para evitar o uso de singletons por completo (e por que eles são muitas vezes referidos como anti-padrão ):

  • Eles são essencialmente glorificado variáveis ??globais
  • Eles podem levar a alta acoplamento entre diferentes partes de um aplicativo
  • Eles podem fazer unidade de teste mais complicado ou impossível (devido à dificuldade em trocar singletons reais com falsos implementações)

Uma alternativa é fazer uso de um 'singleton lógica' em que você criar e inicializar uma única instância de uma classe no segmento principal e passá-lo para os objetos que necessitam dele. Esta abordagem pode se tornar inviável, onde há muitos objetos que você deseja criar como singletons. Neste caso, os objetos díspares podem ser agrupados em um único objeto 'contexto' que é então passada ao redor quando necessário.

Enquanto eu como a solução aceita, eu só encontrei uma outra pista promissora e pensei que eu deveria partilhá-la aqui: One-Time Inicialização (Windows)

Você pode usar um sistema operacional primitivo, como mutex ou seção crítica para garantir passe de inicialização segura no entanto isso vai implicar uma sobrecarga cada vez que o ponteiro singleton é acessado (devido à aquisição de um bloqueio). Também é não portátil.

Há um ponto esclarecendo que você precisa considerar para esta pergunta. Você precisa ...

  1. Que uma e apenas uma instância de uma classe é sempre realmente criado
  2. Muitos instâncias de uma classe pode ser criado, mas deve haver apenas uma verdadeira instância definitiva da classe

Existem muitas amostras na web para implementar esses padrões em C ++. Aqui está um exemplo Code Project

A seguir explica como fazê-lo em C #, mas exatamente o mesmo conceito se aplica a qualquer linguagem de programação que suporte o padrão Singleton

http://www.yoda.arachsys.com/csharp/singleton.html

O que você precisa decidir é wheter você quer inicialização lenta ou não. Preguiçoso meio de inicialização que o objeto contido dentro do singleton é criada na primeira chamada a ele ex:

MySingleton::getInstance()->doWork();

se que is not chamada feita até mais tarde, existe o perigo de uma condição de corrida entre os fios, como explicado no artigo. No entanto, se você colocar

MySingleton::getInstance()->initSingleton();

no início do seu código onde você assumir que seria thread-safe, então você já não são de inicialização atrasada, você vai exigir "alguns" mais processamento energia quando o aplicativo é iniciado. No entanto ele vai resolver um monte de dores de cabeça sobre as condições de corrida se você fazê-lo.

Se você está procurando uma solução mais portátil, e mais fácil, você poderia virar para impulso.

boost :: call_once pode ser usado para a linha de inicialização segura.

Sua bastante simples de usar, e fará parte da próxima C ++ 0x padrão.

A questão não exige que o Singleton é lazy-construído ou não. Desde muitas respostas assumir que, presumo que pela primeira frase discutir:

Dado o fato de que a linguagem em si não é thread-consciência, e mais a técnica de otimização, escrever um portátil confiável c ++ singleton é muito difícil (se não impossível), consulte " C ++ e os perigos da dupla verificado Locking " por Scott Meyers e Andrei Alexandrescu.

Eu vi muitos do resort resposta a objeto sync na plataforma Windows usando CriticalSection, mas CriticalSection só é thread-safe, quando todos os fios está em execução em um único processador, hoje provavelmente não é verdade.

MSDN citar: "Os segmentos de um único processo pode usar um objeto de seção crítica para sincronização-exclusão mútua."

.

http: / /msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx

clearify-lo ainda mais:

Um objecto secção crítica fornece sincronização semelhante à que é fornecida por um objecto de exclusão mútua, excepto que uma secção crítica pode ser utilizado apenas por os fios de um único processo.

Agora, se "preguiçoso-construída" não é um requisito, a seguinte solução é tanto cross-módulo seguro e thread-safe, e até mesmo portátil:

struct X { };

X * get_X_Instance()
{
    static X x;
    return &x;
}
extern int X_singleton_helper = (get_X_instance(), 1);

É cross-módulo-safe porque usamos localmente escopo objeto estático em vez de arquivo / namespace escopo objeto global.

É de thread-safe porque:. X_singleton_helper deve ser atribuído para o valor correto antes de entrar principal ou de DllMain Não lazy-construído também por causa deste fato), nesta expressão a vírgula é um operador, não pontuação

explicitamente o uso "externo" aqui para evitar compilador otimizá-lo para fora (Preocupações sobre artigo Scott Meyers, o grande inimigo é otimizador.), E também fazer ferramenta estática analisar como o PC-Lint manter em silêncio. "Antes principal / DllMain" é Scott Meyer chamou de "parte de inicialização single-threaded" em "Effective C ++ 3ª" Item 4.

No entanto, eu não estou muito certo sobre se o compilador é permitido para otimizar a chamada do get_X_instance () de acordo com o padrão de linguagem, por favor comentário.

Existem muitas maneiras de fazer thread-safe Singleton * inicialização no Windows. Na verdade, alguns deles são até mesmo multi-plataforma. No fio de modo que você ligado a, eles estavam procurando um Singleton que está preguiçosamente construída em C, que é um pouco mais específico, e pode ser um complicado pouco para fazer o certo, dada a complexidade do modelo de memória que você está trabalhando sob .

  • que você nunca deve usar
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top