Estratégias de tratamento de erros em ambientes multissegmentados

5

TL; DR Que estratégias de geração e tratamento de erros você usa no código Multithreaded para uso de outras pessoas e por que você as usa? Se aplicável, indique qual paradigma de programação é útil. Estou mais interessado em ambientes imperativos e simultâneos, mas qualquer um em geral será útil.

Estou escrevendo uma pequena biblioteca de simultaneidade que atualmente é um projeto de aprendizado de estimação / C ++ 11, mas que pode ser usada internamente pelo meu trabalho mais tarde. Em termos de domínio é mais no domínio do DSP e streaming de mídia, mas desde que isso será usado em um mecanismo de jogo eu preciso de tratamento de erros bastante strong.

Meu grande bloco no momento não está me preocupando com o código paralelo e as estruturas de dados, mas com como lidar com erros e gerar relatórios. Minha principal experiência em grandes sistemas é jogos, mas geralmente estou usando bibliotecas, não as projetando. Eu só estou procurando por diferentes estratégias e como elas podem ser usadas em diferentes situações, pois isso é uma grande lacuna no meu conhecimento.

Minha maior área de preocupação é empregar uma estratégia para que, se o programa puder se recuperar, deveria. Se ele se recuperar, deve haver uma maneira de notificar um usuário sobre o que aconteceu com algum tipo de mecanismo. Já tenho algumas estruturas de dados que, embora possam se recuperar, a memória pode vazar se um destrutor falhar, por exemplo.

Algumas abordagens:

  • Trate as exceções para as quais você pode recuperar com segurança de dentro da biblioteca, mas deixe as exceções fatais se propagar para os usuários da biblioteca para indicar que o objeto está agora em um estado indefinido. É a minha abordagem preferida em ambientes de encadeamento único, mas essa abordagem não irá se comunicar com um estado ruim para outros encadeamentos.
  • Quando ocorre uma exceção que colocaria uma estrutura de dados em um estado irrecuperável, desative a estrutura de dados e defina um sinalizador para bloquear mais operações sobre essa estrutura de dados e, em seguida, eleve uma exceção geral para o usuário final. Isso é difícil para algoritmos livres de bloqueio.
  • Encaminhar um estado de erro por meio de cálculos paralelos. Funciona bem para redes de processos da Kahn e outros modelos de simultaneidade de alto nível. Não é tão útil se um primitivo que suporta o modelo de alto nível falhou.
  • Finalize o encadeamento / tarefa que causou a exceção. Funciona bem para dados / cálculos locais de encadeamentos, mas não muito de uma solução para dados compartilhados.

Apenas como nota, sei que uma boa biblioteca provavelmente usará mais do que apenas o que está listado acima. Eu simplesmente não tenho experiência para saber qual estratégia é boa para qualquer sistema suficientemente grande.

    
por BlamKiwi 27.08.2014 / 14:05
fonte

1 resposta

2

Meus dois centavos.

Primeiro, a maioria dos modelos assíncronos que vi nas bibliotecas tendem a me deixar frustrado. Todo mundo parece ter sua própria marca um pouco diferente de assíncrono, e muitas dessas interfaces não são boas. Por isso, gosto de bibliotecas que mantêm tudo sincronizado. Observe que os retornos de chamada ainda podem ser bons. Mas mantenha toda a lógica em um thread; o programador de aplicativos geralmente quer pensar em segmentar além da tarefa que a biblioteca está tentando executar.

Em segundo lugar, uma pequena biblioteca dedicada ao código assíncrono pode ser uma coisa muito boa - desde que esse seja seu único foco. Um padrão que vi e gostei em C # é encadear ações em diferentes encadeamentos, mas escrevê-lo de maneira quase que única, com uma interface fluente. (A nova palavra-chave aguardar é um pouco ao longo das mesmas linhas.) Um lugar comum em que isso ocorre é em despachos para o thread da interface do usuário. Em seguida, forneça uma maneira de lidar com exceções no final, quase como um bloco catch. Então, por exemplo, talvez algo assim:

...
int expensiveResult=-1;
YourThreadLibrary
  .Background(()=>expensiveResult=DoLongRunningTaskToCreate())
  .UI(()=>UpdateUI(expensiveResult))
  .Exception(ex=>LogIt(ex));

Exceções são o caminho a percorrer em C # e você tem GC, então sua situação pode ser diferente. Mas o padrão ainda pode fazer algum sentido.

Eu sei que isso pode parecer muito simplista, mas essas são as ferramentas que eu vi serem genéricas o suficiente para trabalhar em vários problemas. O bom é que uma interface fluente como essa é definitivamente aberta à extensão se escrita corretamente para que você possa adicionar seu .ParallelFailOnAny (params Action []) etc.

    
por 28.08.2014 / 07:32
fonte