Pergunta

O que são as tensões entre multithreading e de exceção de segurança em C ++? Existem boas orientações a seguir? Será que um fio encerrar por causa de uma exceção não pega?

Foi útil?

Solução

Eu acredito que o C ++ padrão não faz qualquer menção de multithreading -. Multithreading é uma característica específica da plataforma

Eu não sei exatamente o que o padrão C ++ diz sobre exceções.Ele em geral, mas de acordo com desta página , o que acontece é definida-plataforma, e você deve descobrir na documentação do seu compilador.

Em um teste rápido e sujo que eu fiz com g ++ 4.0.1 (i686-apple-darwin8-g ++ - 4.0.1 para ser mais específico), o resultado é que terminate() é chamado, que mata todo o programa. O código que usei seguinte:

#include <stdio.h>
#include <pthread.h>

void *threadproc(void *x)
{
  throw 0;

  return NULL;
}

int main(int argc, char **argv)
{
  pthread_t t;
  pthread_create(&t, NULL, threadproc, NULL);

  void *ret;
  pthread_join(t, &ret);

  printf("ret = 0x%08x\n", ret);

  return 0;
}

Compilado com g++ threadtest.cc -lpthread -o threadtest. A produção foi:

terminate called after throwing an instance of 'int'

Outras dicas

C ++ 0x terá Idioma Suporte para o transporte de exceções entre threads de modo que quando um segmento de trabalho gera uma exceção a rosca desova pode pegar ou relançar-lo.

A partir da proposta:

namespace std {

    typedef unspecified exception_ptr;

    exception_ptr current_exception();
    void rethrow_exception( exception_ptr p );

    template< class E > exception_ptr copy_exception( E e );
}

Uma exceção não capturada irá chamar terminate() que por sua vez chama o terminate_handler (que pode ser definido pelo programa). Por padrão, o terminate_handler chama abort().

Mesmo se você substituir o terminate_handler padrão, o padrão diz que a rotina que você fornecer "deve terminar a execução do programa sem retornar ao chamador" (ISO 14882-2003 18.6.1.3).

Assim, em resumo, uma exceção não pega irá terminar o programa não apenas o segmento.

Tanto quanto a segurança do thread vai, como Adam Rosenfield diz, isso é uma coisa plataforma específica que não é abordada pela norma.

Esta é a maior razão que Erlang existe.

Eu não sei o que a convenção é, mas IMHO, seja como Erlang-como possível. Faça pilha objetos imutáveis ??e configurar algum tipo de passagem de mensagens protocolo para se comunicar entre threads. Evitar bloqueios. Certifique-se a passagem da mensagem é exceção-safe. Mantenha o máximo de coisas stateful na pilha.

Como já foi discutido, a simultaneidade (e thread-segurança em particular,) é uma questão de arquitetura, que afeta o modo como você projeta seu sistema e sua aplicação.

Mas eu gostaria de aproveitar a sua pergunta sobre tensão entre exceção, segurança e thread-segurança.

Ao nível da classe thread-segurança requer alterações na interface. Assim como exceção à segurança faz. Por exemplo, é habitual para as classes para retornar referências a variáveis ??internas, dizem:

class Foo {
public:
  void set_value(std::string const & s);

  std::string const & value() const;
};

Se Foo é compartilhada por vários segmentos, aguarda incomodá-lo. Naturalmente, você poderia colocar um mutex ou outro bloqueio para acesso Foo. Mas logo, todos os programadores C ++ iria querer envolver Foo em um "ThreadSafeFoo". Meu argumento é que a interface para Foo deve ser alterado para:

class Foo {
public:
  void set_value(std::string const & s);

  std::string value() const;
};

Sim, é mais caro, mas pode ser feita thread-safe com fechaduras dentro Foo. IMNSHO isto cria uma certa quantidade de tensão entre o fio-de segurança e excepção-segurança. Ou, pelo menos, você precisa para realizar mais análises como cada classe usado como um compartilhados necessidades de recursos a serem examinadas sob as duas luzes.

Um exemplo clássico (não me lembro onde eu vi pela primeira vez) está na biblioteca std.

Veja como você pop algo de uma fila:

T t;
t = q.front(); // may throw
q.pop();

Esta interface é um pouco obtuso em comparação com:

T t = q.pop();

Mas é feito porque a atribuição de cópia T pode jogar. Se a cópia joga depois do pop acontece, esse elemento é perdido da fila, e nunca pode ser recuperado. Mas desde que a cópia acontece antes de o elemento é estourado, você pode colocar a manipulação arbitrária em torno da cópia da frente () em blocos try / catch.

A desvantagem é que você não pode implementar uma fila que é o segmento de seguros com a interface do std :: fila por causa das duas etapas envolvidas. O que é bom para a segurança de exceção (separando os passos que pode jogar), agora é ruim para multithreading.

O seu principal salvador em segurança exceção é que as operações de ponteiro não são de lance. Da mesma forma, as operações de ponteiro podem ser feitas atômica na maioria das plataformas, de modo que muitas vezes pode ser o seu salvador em código multithreaded. Você pode ter seu bolo e comê-lo, mas é realmente difícil.

Há duas questões que eu observei:

  • em g ++ no Linux, o assassinato de um thread (pthread_cancel) é realizado por lançar uma exceção "desconhecido". Por um lado, que lhe permite limpar muito bem quando o segmento está sendo morto. Por outro lado, se você pegar essa exceção e não relançar-lo, seus fins de código com abort (). Portanto, se você ou qualquer uma das bibliotecas que você usa tópicos matar, você não pode ter

    catch (...)

sem

throw;

em seu código de rosca. Aqui é uma referência a esse comportamento na web:

  • Às vezes você precisa para transferir exceção entre threads. Esta não é uma coisa fácil de fazer -. Acabamos fazendo somehack, quando a solução adequada é o tipo de triagem / demarshalling você usaria entre os processos

Eu não recomendo deixar qualquer exceção permanecem não capturada. Enrole suas funções de rosca de nível superior no catch-all manipuladores que podem mais graciosamente (ou pelo menos verbosely) encerrar o programa.

Eu acho que a coisa mais importante é lembrar que exceções.Ele de outros tópicos não aparecem para o usuário ou jogado no segmento principal. Então você tem que deformar todo o código que deve ser executado em tópicos diferentes do que o segmento principal com blocos try / catch.

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