Pergunta

eu correr em um problema que parece preocupante para mim. Parece que eu encontrei uma situação que é bastante fácil de contornar, mas que poderia levar a problemas se a) Eu tenho um lapso na concentração durante a programação ou b) alguém começa outra pessoa implementação meus interfaces e não sabem como lidar com esta situação.

Aqui está a minha configuração básica:

Eu tenho uma classe abstrata que eu estou usando como uma interface genérica para vários tipos de dados. Eu adotei o não-virtual paradigma de interface pública (Sutter, 2001) juntamente com bloqueio escopo para fornecer alguma segurança de segmentos. Uma classe de exemplo de interface seria algo parecido com isso (eu deixei de fora detalhes sobre bloqueio escopo ea implementação mutex, como eu não acho que eles são relevantes):

class Foo
{
public:
    A( )
    {
        ScopedLock lock( mutex );
        aImp( );
    }
    B( )
    {
        ScopedLock lock( mutex );
        bImp( );
    }
protected:
    aImp( ) = 0;
    bImp( ) = 0;
}

Em seguida, é até o usuário para implementar AIMP e BIMP, que é onde o problema vem em Se AIMP realizar alguma operação que utiliza BIMP, é extremamente fácil (e quase lógico, em algum sentido) para fazer isso:.

class Bar
{
protected:
    aImp( )
    {
        ...
        B( );
        ...
    }
    bImp( )
    {
        ...
    }
}

impasse. Claro, a solução fácil para isso é sempre chamar as funções virtuais protegidas ao invés de suas variantes públicas (substitua B () com BIMP () no trecho acima). Mas ainda parece longe para fácil de pendurar-me se eu cometer um erro, ou pior ainda permitir que outros para se enforcar.

Alguém tem alguma maneira de tentar quer parar um implementador da classe abstrata de chamar essas funções públicas em tempo de compilação, ou de outra forma de ajuda para evitar a solução de impasse?

Apenas por diversão, alguns semáforos permitir a operação que irá evitar problemas de impasse. Como um exemplo, se eu implementar isso usando as funções do Windows EnterCriticalSection e LeaveCriticalSection, não há problema. Mas eu prefiro evitar funcionalidade específica da plataforma. Atualmente estou usando boost :: mutex e boost :: shared_mutex na minha implementação bloqueio escopo, e, tanto quanto eu vi isso não tenta impasse evitar (o que eu acho que eu quase preferir).

Foi útil?

Solução

Usando herança privado potencialmente resolver o seu problema:

class Foo
{
public:
  void A( )
    {
      ScopedLock lock( mutex );
      aImp( );
    }
  void B( )
    {
      ScopedLock lock( mutex );
      bImp( );
    }

protected:
  virtual void aImp( ) = 0;
  virtual void bImp( ) = 0;
};

class FooMiddle : private Foo
{
public:
  using Foo::aImp;
  using Foo::bImp;
};

class Bar : public FooMiddle
{
  virtual void aImpl ()
  {
    bImp ();
    B ();                   // Compile error - B is private
  }
};

Derivando de Foo privada, e em seguida, usando garante FooMiddle que Bar não tem acesso a A ou B. No entanto, bar ainda é capaz de substituir AIMP e BIMP, e os que usam declarações FooMiddle significa que estes ainda podem ser chamados de Bar.

Como alternativa, uma opção que irá help , mas não resolver o problema é usar o padrão Pimpl. Você iria acabar com algo como se segue:

class FooImpl
{
public:
  virtual void aImp( ) = 0;
  virtual void bImp( ) = 0;
};

class Foo
{
public:
  void A( )
    {
      ScopedLock lock( mutex );
      m_impl->aImp( );
    }
  void B( )
    {
      ScopedLock lock( mutex );
      m_impl->bImp( );
    }

private:
  FooImpl * m_impl;
}

A vantagem é que nas classes decorrentes FooImpl, eles já não têm um objeto "Foo" e por isso não pode facilmente chamar "A" ou "B".

Outras dicas

Seu mutex não deve ser um mutex recursiva. Se seu não um mutex recursivo, uma segunda tentativa para travar o mutex no mesmo segmento resultará em que o bloqueio fio. Desde esse segmento trancou a exclusão mútua, mas é bloqueado em que mutex, você tem um impasse.

Você provavelmente vai querer olhar em:

boost::recursive_mutex

http://www.boost.org/doc /libs/1_32_0/doc/html/recursive_mutex.html

É suposto implementar o comportamento mutex recursiva cruz platform.Note Win32 do CRITICAL_SECTION (utilizado via Enter / LeaveCriticalSection) são recursiva, o que criaria o comportamento que você descreve.

Enquanto um bloqueio recursiva iria resolver o seu problema, eu sempre senti que, embora, por vezes, necessário, em muitas situações um bloqueio recursiva é usada como um caminho mais fácil, travando demais.

Seu código postada é obviamente simplificado para fins de demonstração, então eu não tenho certeza se ele vai aplicar.

Como exemplo, digamos que usando o recurso X não é threadsafe. Você tem algo semelhante.

A() {
   ScopedLock
   use(x)
   aImp()
   use(x)
}

aImp() {
   ScopedLock
   use(x)
}

Obviamente, isso resultaria em um impasse.

Usando suas fechaduras muito mais estreito no entanto, iria eliminar o problema. Usando bloqueios em uma tão pequena margem de manobra possível é sempre uma boa ideia, tanto por razões de desempenho, como evitar impasse.

A() {
   {
      ScopedLock
      use(x)
   }
   aImp()
   {
      ScopedLock
      use(x)
   }
}

Você começa a idéia.

Estou ciente isso nem sempre é possível (ou levaria ao código horrivelmente innefficient), sem saber mais detalhes que eu não sei se ele se aplica ao seu problema. Mas achei que foi vale postagem de qualquer maneira.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top