Atividade:
| ||||||||||||||||||||||||||||
Finalidade
|
|
Passos
|
|
| Artefatos Informados: | Artefatos Resultantes: |
| Papel: Arquiteto de Software | |
| Diretrizes: | |
| Conceitos: | |
| Pontos de Verificação: | |
| Mentor de Ferramentas: | |
| Detalhamentos do Fluxo de Trabalho: |
Objetos ativos (ou seja, instâncias de classes ativas) são usados para representar threads simultâneos de execução no sistema: teoricamente, cada objeto ativo tem seu próprio thread de controle e, convencionalmente, ele é a raiz do frame de pilhas de execução. O mapeamento de objetos ativos para threads ou processos reais do sistema operacional pode variar de acordo com os requisitos de resposta e será influenciado considerando a carga de alternância de contexto. Por exemplo, diversos objetos ativos, combinados por um simples programador, podem compartilhar um único thread do sistema operacional, o que dará a impressão de estarem em execução simultânea. Entretanto, se algum objeto ativo exibir comportamento de bloqueio, por exemplo, ao executar operações síncronas de entrada e saída, outros objetos ativos do grupo poderão responder aos eventos ocorridos enquanto o thread do sistema operacional estiver bloqueado.
Por outro lado, se cada objeto ativo tiver seu próprio thread do sistema operacional, a resposta será maior, desde que os recursos de processamento não sofram um impacto negativo causado pela carga decorrente da alternância de contexto.
Nos sistemas de tempo real, Artefato: Cápsulas é a maneira recomendada de modelar a simultaneidade. Como as classes ativas, cada cápsula tem teoricamente seu próprio thread de controle. No entanto, as cápsulas têm encapsulamento adicional e semântica composicional para facilitar a modelagem de problemas complexos em tempo real.
Essa atividade define uma arquitetura de processo para o sistema em termos de classes ativas e suas instâncias, e do relacionamento delas com os threads e processos do sistema operacional.
Da mesma forma, para os sistemas de tempo real, a arquitetura de processo será definida em termos de cápsulas e de um mapeamento associado desses sistemas para os processos e threads do sistema operacional.
No início da fase de Elaboração, essa arquitetura é bastante preliminar. Mais adiante, os processos e threads já devem estar bem definidos.
Os resultados dessa atividade são capturados no modelo de design - em particular, na visão de processos (consulte Conceitos: Visão de Processos).
Finalidade
|
Durante Atividade: Identificar Elementos de Design, os requisitos de simultaneidade orientados principalmente por demandas corriqueiras de simultaneidade no domínio de problemas serão considerados.
O resultado é a obtenção de um conjunto de classes ativas que representam threads lógicos de controle no sistema.
Em sistemas de tempo real, essas classes são representadas por Artefato: Cápsulas.
Nesse passo, devemos considerar outras fontes de requisitos de simultaneidade - aquelas impostas pelos requisitos não-funcionais do sistema.
Os requisitos de simultaneidade são orientados por:
Esse raciocínio se aplica também às cápsulas, quando elas forem utilizadas no design de sistemas de tempo real.
Como ocorre com muitos problemas relativos à arquitetura, esses requisitos podem, de alguma forma, ser mutuamente exclusivos. Pelo menos inicialmente, não é comum ocorrerem conflitos entre requisitos. A ordenação dos requisitos em termos de importância ajudará a solucionar o conflito.
Finalidade
|
| Conceitos: Simultaneidade |
A abordagem mais simples é alocar todos os objetos ativos em um thread ou processo comum e usar um programador simples de objetos ativos. Esse procedimento minimizará a carga decorrente da alternância de contexto. Entretanto, em alguns casos, pode ser necessário distribuir os objetos ativos em um ou mais threads ou processos.
Certamente, esse é o caso da maioria dos sistemas de tempo real, nos quais as cápsulas usadas para representar os threads lógicos, em alguns casos, têm que cumprir requisitos rígidos de programação.
Se um objeto ativo, que esteja compartilhando um thread do sistema operacional com outros objetos ativos, fizer uma chamada síncrona para outro processo ou thread, e essa chamada bloquear o thread do sistema operacional do objeto que fez a chamada, todos os outros objetos ativos localizados no processo de disparo serão suspensos automaticamente. Agora, esse não deve ser o caso: uma chamada síncrona, do ponto de vista do objeto ativo, pode ser tratada assincronamente pela perspectiva do programador que controla o grupo de objetos ativos - o programador suspende o objeto ativo que está fazendo a chamada (à espera da conclusão de sua chamada síncrona) e, então, programa a execução de outros objetos ativos.
Quando a operação 'síncrona' original estiver concluída, o objeto ativo do disparo prosseguirá. Contudo, essa abordagem nem sempre é possível, pois pode não ser viável projetar o programador para interceptar todas as chamadas síncronas antes de serem bloqueadas. Observe que uma invocação síncrona entre objetos ativos que utilizem o mesmo processo ou thread do sistema operacional pode, por questões genéricas, ser tratada pelo programador dessa maneira - e seu efeito sobre uma chamada de procedimento pode ser idêntico, do ponto de vista do objeto ativo que fez o disparo.
Sendo assim, podemos concluir que os objetos ativos devem ser agrupados em processos ou threads de acordo com a necessidade de serem executados simultaneamente com invocações síncronas que bloqueiam o thread. Em outras palavras, só há um momento em que um objeto ativo deve ser empacotado no mesmo processo ou thread com um outro objeto que utilize chamadas síncronas que bloqueiam o thread. É quando ele não precisa ser executado com esse objeto e aceita não ser executado enquanto o outro objeto está bloqueado. Em casos extremos, quando a resposta é um fator crítico, essa situação pode exigir um thread ou processo separado para cada objeto ativo.
Para os sistemas de tempo real, as interfaces de cápsulas baseadas em mensagens pretendem que é mais simples conceber um programador que garanta, pelo menos nas comunicações entre cápsulas, que os threads do sistema operacional de suporte nunca sejam bloqueados, mesmo quando uma cápsula se comunica sincronamente com outra. Porém, ainda assim, é possível que uma cápsula envie uma solicitação diretamente para o sistema operacional, como, por exemplo, para uma espera síncrona que pode bloquear o thread. É preciso estabelecer convenções para os serviços de nível inferior disparados pelas cápsulas que evitam esse comportamento, caso as cápsulas compartilhem um mesmo thread (e usem um programador simples para simular simultaneidade).
Como regra geral, nas situações citadas acima é melhor usar threads leves do que processos completos, já que a carga é menor. Entretanto, ainda podemos aproveitar algumas características especiais dos processos em certos casos especiais. Como os threads compartilham o mesmo espaço de endereço, eles são naturalmente mais arriscados do que os processos. Se houver a preocupação com substituições acidentais, é melhor usar processos. Além disso, já que os processos representam unidades independentes de recuperação na maioria dos sistemas operacionais, pode ser útil alocar objetos ativos para os processos de acordo com sua necessidade de recuperação, independentemente um do outro. Isso quer dizer que todos os objetos ativos que precisam de recuperação podem ser incluídos no mesmo processo.
Para cada fluxo de controle separado exigido pelo sistema, crie um processo ou thread (processo leve). Um thread deve ser usado nos casos em que há necessidade de um fluxo de controle aninhado (isto é, se, em um processo, for preciso um fluxo de controle independente no nível de subtarefa).
Por exemplo, podemos afirmar (não necessariamente por ordem de importância) que são necessários threads de controle para:
Exemplo
No Sistema de Caixa Eletrônico, os eventos assíncronos devem ser tratados de três maneiras: o usuário do sistema, os dispositivos do sistema (no caso de um congestionamento no caixa eletrônico, por exemplo) ou a rede do sistema (no caso de um shutdown originado na rede). Para tratar desses eventos assíncronos, podemos definir três threads de execução separados no próprio sistema de caixa eletrônico, como mostrado abaixo, com o uso de classes ativas em UML.

Processos e Threads no Sistema de Caixa Eletrônico
Finalidade
|
Cada processo ou thread de controle deve ser criado e destruído. Em uma arquitetura com um único processo, a criação de processos ocorre quando o aplicativo é inicializado. A destruição de processos ocorre quando o aplicativo é encerrado. Em arquiteturas multiprocesso, os processos (ou threads) novos geralmente são desdobrados ou separados do processo inicial criado pelo sistema operacional quando o aplicativo é inicializado. Esses processos também devem ser destruídos explicitamente.
A seqüência de eventos que leva à criação e destruição dos processos deve ser determinada e documentada, como também o mecanismo de criação e exclusão.
Exemplo
No Sistema de Caixa Eletrônico, um processo principal é iniciado, e ele é responsável pela coordenação do comportamento de todo o sistema. Por sua vez, ele gera vários threads de controle subordinados para monitorarem diversas áreas do sistema: os dispositivos do sistema e os eventos originados pelo cliente e pela Rede do Sistema de Caixa Eletrônico. A criação desses processos e threads pode ser demonstrada com classes ativas em UML. A criação de instâncias dessas classes ativas pode ser ilustrada em um diagrama de seqüência como este:

Criação de processos e threads durante a inicialização do sistema
Finalidade
|
Os mecanismos de comunicação entre processos (IPC) permitem que sejam enviadas mensagens entre objetos em execução em processos separados.
Os mecanismos comuns de comunicação entre processos incluem:
A escolha do mecanismo IPC mudará a maneira como o sistema é modelado. Em uma "arquitetura de barramento de mensagens", por exemplo, não há necessidade de associações explícitas entre os objetos para o envio de mensagens.
Finalidade
|
Geralmente, os mecanismos de comunicação entre processos são escassos. Semáforos, memória compartilhada e caixas de correio normalmente têm tamanho fixo e o seu aumento é uma tarefa bastante dispendiosa. RPC, transmissão de mensagens e eventos aumentam larguras de banda de rede cada vez mais escassas. Quando o sistema ultrapassa um limite de recurso, seu desempenho diminui de forma não linear: depois que um recurso escasso é totalmente utilizado, solicitações subseqüentes desse recurso podem provocar um efeito inesperado.
Se houver excesso de solicitação pelos recursos escassos, será importante considerar várias estratégias:
Independentemente da estratégia escolhida, o desempenho do sistema deverá diminuir gradativamente (em vez de ocorrer uma pane) e oferecer um feedback adequado ao administrador do sistema para permitir a solução do problema (se possível) no campo em que o sistema está implantado.
Se o sistema precisar de configuração especial do ambiente de tempo de execução para aumentar a disponibilidade de um recurso crítico (controle por meio da reconfiguração do kernel do sistema operacional), o programa de instalação terá que fazê-lo automaticamente ou instruir o administrador do sistema para que o faça antes de o sistema entrar em operação. Por exemplo, o sistema talvez precise ser reinicializado para que a mudança seja efetivada.
Finalidade
|
Os processos conceituais devem ser mapeados para as construções específicas do ambiente operacional. Em muitos ambientes, há várias opções de tipos de processos, geralmente processos e threads. As opções se basearão no grau de acoplamento (os processos são independentes e os threads são executados no contexto do processo em que estão contidos) e nos requisitos de desempenho do sistema (comunicação entre processos estabelecida entre threads é geralmente mais rápida e mais eficiente do que a estabelecida somente entre processos).
Em vários sistemas, pode haver um número máximo de threads por processo ou de processos por nó. Esses limites podem não ser absolutos, mas podem ser limites práticos impostos pela disponibilidade dos recursos escassos. Os threads e processos que já estiverem em execução em um nó-alvo precisarão ser considerados com os threads e processos propostos na arquitetura de processos. Os resultados da etapa anterior, Alocar Recursos de Coordenação entre Processos, precisam ser considerados quando for feito o mapeamento para verificar se um novo problema de desempenho não está sendo criado.
Finalidade
|
As instâncias de determinada classe ou determinado subsistema devem ser executadas em pelo menos um processo. Na verdade, elas podem ser executadas em processos distintos. O processo oferece um ambiente de execução para a classe ou para o subsistema.
Com a utilização simultânea de duas estratégias diferentes, determinamos o volume "correto" de simultaneidade e o conjunto "correto" de processos:
Esse não é um processo linear, determinista, que conduz a uma visão de processos ideal. Ele requer algumas iterações até ser obtido um ajuste aceitável.
Exemplo
O diagrama abaixo ilustra como as classes do sistema de caixa eletrônico são distribuídas entre os processos e threads do sistema.

Mapeamento de classes nos processos do sistema de caixa eletrônico
|
Rational Unified Process
|