O Adaptive Communication Environment (ACE) é uma estrutura de código aberto orientada
a objeto de alto desempenho e C++ uma biblioteca de classe
que simplifica o desenvolvimento de aplicativos de rede. O conjunto de ferramentas
ACE é uma combinação de uma camada do sistema operacional e uma coleção de
C++ fachadas que encapsulam a rede API. Este artigo mostra
como projetar aplicativos de rede orientados a objeto, simultâneos e de alto desempenho
usando os encadeamentos ACE. Para obter uma descrição completa do ACE, incluindo como
efetuar o download e instalar o conjunto de ferramentas, consulte Recursos.
Classes ACE para criação e gerenciamento de encadeamentos
As classes a seguir estão envolvidas na criação e gerenciamento de múltiplos encadeamentos dentro de um processo:
ACE_Thread_Manager: Esta é a classe principal responsável pela criação, gerenciamento e sincronização dos encadeamentos. Todos os sistemas operacionais possuem suas próprias sutilezas na manipulação dos encadeamentos: Esta classe do wrapper deixa estas peculiaridades opacas para o desenvolvedor de aplicativos.ACE_Sched_Params: Esta classe é utilizada para gerenciar a prioridade do planejamento dos encadeamentos individuais, que é definida como parte do cabeçalho ace/Sched_Params.h na distribuição da origem ACE. As políticas de planejamento variam e podem ser round robin ou primeiro a chegar, primeiro a ser servido.ACE_TSS: O uso de variáveis globais em um aplicativo multiencadeado é um problema de sincronização. A classeACE_TSSoferece um padrão de armazenamento específico do encadeamento, fornecendo a abstração de dados que são logicamente globais para o programa, mas na realidade são privados para cada encadeamento. A classeACE_TSSfornece o métodooperator(), o que, por sua vez, oferece os dados específicos do encadeamento.
Entendendo a classe do gerente de encadeamento
As APIs de encadeamento de sistemas operacionais nativos não são móveis: Há diferenças
sintáticas e semânticas. Por exemplo, os métodos ® pthread_create()
do UNIX e o ® CreateThread() do Windows criam um encadeamento,
mas com diferenças sintáticas. A classe ACE_Thread_Manager
vem com os seguintes recursos:
- Ela pode criar um ou mais encadeamentos, cada um deles executando sua própria função designada.
- Ela pode gerenciar os encadeamentos relacionados como uma coleção coesa, conhecida como um grupo de encadeamentos.
- Ela gerencia a prioridade de planejamento dos encadeamentos individuais.
- Ela permite a sincronização entre os encadeamentos.
- Ela pode alterar atributos do encadeamento como o tamanho de pilha.
Os métodos notáveis da classe ACE_Thread_Manager são descritos
na Tabela 1.
Tabela 1. Métodos da classe ACE_Thread_Manager
| Nome do método | Descrição |
|---|---|
instance | A classe ACE_Thread_Manager é um singleton, este
método é utilizado para acessar a cópia exclusiva do Thread Manager.
|
spawn | Este método cria um novo encadeamento, um argumento de entrada aplicado a ele sendo o ponteiro de função
C/C++ que faz o trabalho designado do
aplicativo.
|
exit | Este método sai de um encadeamento e libera todos os recursos do encadeamento. |
spawn_n | Este método cria múltiplos encadeamentos que pertencem ao mesmo grupo de encadeamento. |
close | Este método encerra e libera os recursos de todos os encadeamentos criados. |
suspend | Determinado o ID do encadeamento, o gerente de encadeamento o suspende. |
resume | O gerente de encadeamento retoma o encadeamento anteriormente suspenso. |
Variações no uso da classe ACE_Thread_Manager
É possível utilizar a classe ACE_Thread_Manager como
um singleton ou criar múltiplas instanciações da classe. Para o singleton, é
possível usar uma chamada para o método instance para
obter acesso. Em situações em que é necessário gerenciar múltiplos grupos de encadeamento,
é útil possuir diferentes classes de gerente de encadeamento, cada uma controlando
seu próprio conjunto de encaminhamentos.
Observe o exemplo na Lista 1, que cria um encadeamento.
Lista 1. Criando um encadeamento usando a classe ACE_Thread_Manager
#include "ace/Thread_Manager.h"
#include <iostream>
void thread_start(void* arg)
{
std::cout << "Running thread..\n";
}
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
ACE_Thread_Manager::instance()->spawn((ACE_THR_FUNC)thread_start);
return 0;
}
|
A Lista 2 mostra o protótipo para o método
spawn(), originado do
ace/Thread_Manager.h.
Lista 2. O protótipo para o método de criação do ACE_Thread_Manager::
int spawn (ACE_THR_FUNC func,
void *arg = 0,
long flags = THR_NEW_LWP | THR_JOINABLE | THR_INHERIT_SCHED,
ACE_thread_t *t_id = 0,
ACE_hthread_t *t_handle = 0,
long priority = ACE_DEFAULT_THREAD_PRIORITY,
int grp_id = -1,
void *stack = 0,
size_t stack_size = ACE_DEFAULT_THREAD_STACKSIZE,
const char** thr_name = 0);
|
O número de argumentos absolutos necessários para criar apenas um encadeamento básico podem parecer impressionantes para os usuários na primeira utilização, então, observemos melhor os argumentos individuais e por que eles são necessários:
ACE_THR_FUNC func: Esta função é solicitada quando o encadeamento é criado.void* arg:Um argumento para a função solicitada quando o encadeamento é criado,void*significa que os usuários estão livres para passar em qualquer tipo de dado específico do aplicativo ou ainda unir múltiplos argumentos em um único dado usando uma estrutura.long flags: Várias propriedades do encadeamento criado são definidas usando a variávelflags. Os atributos individuais sãobitwiseou-ed. A Tabela 2 mostra alguns dos atributos.ACE_thread_t *t_id: Use esta função para acessar o ID do encadeamento criado. Cada encadeamento possui um ID único.long priority: Esta é a prioridade com que os encadeamentos são criados.int grp_id: Caso seja fornecido, este argumento indica se o encadeamento deve fazer parte de algum grupo de encadeamento existente. Caso contrário, caso o-1é aceito como o argumento, um novo grupo de encadeamento é criado e o encadeamento criado é adicionado a ele.void* stack: Este é o ponteiro para a área de pilha pré-alocada. Se0for fornecido como o argumento, é solicitado que o sistema operacional forneça a área de pilha do encadeamento criado.size_t stack_size: Este argumento indica o tamanho da pilha do encadeamento em bytes. Se0for fornecido como o argumento para ostack pointer (item 7), é solicitado que o sistema operacional indique uma área com o tamanhostack_size.const char** thr_name: Este argumento é relevante apenas em plataformas como o VxWorks que possuem a capacidade de nomear os encadeamentos. Ele é ignorado para a maioria dos propósitos nos sistemas UNIX.
Tabela 2. Propriedades do encadeamento e suas descrições
| Sinalizador de criação de encadeamento | Descrição |
|---|---|
THR_CANCEL_DISABLE | Não permite que o encadeamento seja cancelado. |
THR_CANCEL_ENABLE | Permite que o encadeamento seja cancelado. |
THR_DETACHED | Cria um encadeamento assíncrono. O status de saída do encadeamento não poderia estar disponível em nenhum outro encadeamento. Os recursos do encadeamento são exigidos pelo sistema operacional sempre que o encadeamento for terminado. |
THR_JOINABLE | Permite que o status de saída do novo encadeamento seja disponibilizado para outros encadeamentos. Este também é o atributo padrão nos encadeamentos ACE criados. Quando este tipo de encadeamento expira, os recursos não são exigidos pelo sistema operacional até que algum outro encadeamento una-se a ele. |
THR_NEW_LWP | Cria um encadeamento do nível do kernel explicito (como oposição ao encadeamento do nível do usuário). |
THR_SUSPENDED | Cria um novo encadeamento no estado suspenso. |
A Lista 3 fornece um exemplo que cria múltiplos encadeamentos
usando o método spawn_n da classe do gerente de
encadeamento.
Lista 3. Criando múltiplos encadeamentos usando a classe ACE_Thread_Manager
#include "ace/Thread_Manager.h"
#include <iostream>
void print (void* args)
{
int id = ACE_Thread_Manager::instance()->thr_self();
std::cout << "Thread Id: " << id << std::endl;
}
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
ACE_Thread_Manager::instance()->spawn_n(
4, (ACE_THR_FUNC) print, 0, THR_JOINABLE | THR_NEW_LWP);
ACE_Thread_Manager::instance()->wait();
return 0;
}
|
Mecanismo alternativo de criação de encadeamento no ACE
Esta seção trata de um esquema de gerenciamento/criação alternativo de encadeamento
no ACE, o esquema que não precisa do nível explicito de controle detalhado do gerente
de encadeamento. Por padrão, cada processo é criado com um único encadeamento que
inicia e é encerrado com a função main . Quaisquer encadeamentos
extras precisam da criação explícita. Uma maneira alternativa de criar os encadeamentos
é com a criação de uma subclasse da classe ACE_Task_Base
pré-definida, então substituir o método svc() . O novo
encadeamento é iniciado no método svc() e é encerrado
quando o método svc() retorna. Antes de prosseguir com
a explicação, observe a fonte mostrada na Lista 4.
Lista 4. Criando um encadeamento usando o ACE_Task_Base::svc
#include “ace/Task.h”
class Thread_1 : public ACE_Task_Base {
public:
virtual int svc( ) {
std::cout << “In child’s thread\n”;
return 0;
}
};
int main ( )
{
Thread_1 th1;
th1.activate(THR_NEW_LWP|THR_JOINABLE);
th1.wait();
return 0;
}
|
O comportamento específico do aplicativo do encadeamento está codificado no método
svc() . Para executar o encadeamento, o método
activate() (declarado e definido como parte da classe
ACE_Task_Base ) é solicitado. Quando o encadeamento
estiver ativado, a função main() espera que o encaminhamento
filho conclua a execução. É isso que o método wait()
tenta obter: O encadeamento principal é bloqueado até que o
Thread_1 seja completado. Esta espera é um recurso
necessário, caso contrário, o encadeamento principal poderia planejar o encadeamento
filho e criado uma saída. O tempo de execução C , ao
observar a saída do encadeamento principal, deveria destruir todos os encaminhamentos
filho, especificamente, o encadeamento filho provavelmente nunca poderia ter sido planejado ou executado.
Entendendo a classe ACE_Task_Base mais detalhadamente
Aqui há um exame relativamente detalhado em alguns dos métodos no
ACE_Task_Base.
A Lista 5 mostra o protótipo do método activate()
.
Lista 5. O protótipo do método ACE_Task_Base::activate
virtual int activate (long flags = THR_NEW_LWP | THR_JOINABLE | THR_INHERIT_SCHED,
int n_threads = 1,
int force_active = 0,
long priority = ACE_DEFAULT_THREAD_PRIORITY,
int grp_id = -1,
ACE_Task_Base *task = 0,
ACE_hthread_t thread_handles[ ] = 0,
void *stack[ ] = 0,
size_t stack_size[ ] = 0,
ACE_thread_t thread_ids[ ] = 0,
const char* thr_name[ ] = 0);
|
É possível utilizar o método activate() para criar
um ou mais encadeamentos do controle, cada um dos quais chama o mesmo método
svc() , e todos sendo executados com a mesma
prioridade com o mesmo ID de grupo. Eis uma breve descrição de alguns dos
argumentos de entrada:
long flags: Consulte a Tabela 2.int n_threads: Cria o número de encadeamentos especificados pelon_threads.int force_active: Se este sinalizador for True e houver encadeamentos existentes criados pela tarefa, então todos os encadeamentos recentemente criados devem compartilhar o ID do grupo dos anteriormente criados e ignorar o valor conforme passado para o métodoactivate().long priority: Este argumento designa uma prioridade para o encaminhamento ou coleção de encaminhamentos. Os valores de prioridade de planejamento são específicos do sistema operacional e é uma aposta segura para o valor padrão a ser seguido doACE_DEFAULT_THREAD_PRIORITY.ACE_hthread_t thread_handles: Sethread_handles != 0, então, após a criação dos n encadeamentos, este array será designado para os identificadores do encadeamento individual.void* stack: Caso seja especificado, este argumento indica um array dos ponteiros para a base das pilhas para os encaminhamentos individuais.size_t stack_size: Caso seja especificado, este argumento indica um array dos números inteiros indicando o tamanho das pilhas dos encaminhamentos individuais.ACE_thread_t thread_ids: Sethread_ids != 0, presume-se que é um array que suspenderá os IDs dos n encadeamentos criados recentemente.
A Lista 6 mostra algumas outras rotinas úteis na classe
ACE_Task_Base .
Lista 6. Rotinas diversas do ACE_Task_Base
// Bloqueia o encadeamento principal até que todos os encadeamentos desta tarefa sejam concluídos virtual int wait (void); // Suspende uma tarefa virtual int suspend (void); // Continua a tarefa suspensa. virtual int resume (void); // Obtém o número de encadeamentos ativos em uma tarefa size_t thread_count (void) const; // Retorna o id do último encadeamento cuja saída fez com que a contagem de encadeamentos // desta tarefa fosse 0. Um status de retorno zero significa que o resultado é // desconhecido. Talvez não haja encadeamentos planejados. ACE_thread_t last_thread (void) const; |
Para criar um encadeamento em um estado suspenso (em oposição a uma suspensão
explicitamente utilizando a chamada de método suspend() ),
é necessário passar o sinalizador THR_SUSPENDED para o método
activate() . É possível continuar o encadeamento ao chamar o
método resume() , conforme mostrado na
Lista 7.
Lista 7. Suspensão e continuação dos encadeamentos
Thread_1 th1; th1.activate(THR_NEW_LWP|THR_JOINABLE|THR_SUSPENDED); …// código no encadeamento principal th1.resume(); …// o código continua no encadeamento principal |
Uma outra observação dos sinalizadores de encadeamento
São possíveis dois tipos de encadeamento: encadeamentos do nível do kernel e encadeamentos do nível de usuário. Se o método
activate() for chamado sem nenhum argumento, o padrão
é criar um encadeamento do nível do kernel. Os encadeamentos do nível do kernel
interagem diretamente com o sistema operacional e são planejados pelo planejador
do nível do kernel. Em contrapartida, os encadeamentos do nível do usuário operam
no escopo do processo e são encadeamentos do nível do kernel "designados" de acordo com o necessário para completar algumas tarefas. O sinalizador
THR_NEW_LWP, que é o argumento padrão para o método
activate() , sempre garante que o novo encadeamento
criado seja um encadeamento do nível do kernel.
O ACE fornece um gancho de inicialização global do encadeamento que permite que o
usuário realize qualquer tipo de operação que seja globalmente aplicável para todos
os encadeamentos. Para criar um gancho de inicialização, é necessário criar uma subclasse da classe pré-definida
ACE_Thread_Hook e fornecer a definição para o método
start() . O método
start() aceita dois argumentos: um ponteiro para uma
função definida pelo usuário e uma void*, que é aceito
por essa função definida pelo usuário. Para registrar o gancho é necessário chamar
o método estático ACE_Thread_Hook::thread_hook, conforme
mostrado na Lista 8.
Lista 8. Usando ganchos de encadeamento globais
#include "ace/Task.h"
#include "ace/Thread_Hook.h"
#include <iostream>
class globalHook : public ACE_Thread_Hook {
public:
virtual ACE_THR_FUNC_RETURN start (ACE_THR_FUNC func, void* arg) {
std::cout << "In created thread\n";
(*func)(arg);
}
};
class Thread_1 : public ACE_Task_Base {
public:
virtual int svc( ) {
std::cout << "In child's thread\n";
return 0;
}
};
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
globalHook g;
ACE_Thread_Hook::thread_hook(&g);
Thread_1 th1;
th1.activate();
th1.wait();
return 0;
}
|
Observe que o ponteiro ACE_THR_FUNC que é automaticamente
aceito pelo gancho de inicialização é igual à função que poderia ter sido chamada
quando o método activate() do encadeamento é realizado.
Os resultados do código acima são as mensagens:
In created thread In child’s thread |
Este artigo fez uma primeira observação sobre a criação e o gerenciamento dos encadeamentos utilizando a estrutura ACE. A estrutura ACE possui vários outros recursos úteis, como mutexes, blocos de segurança para sincronização, memória compartilhada e serviços de rede. Consulte Recursos para obter detalhes.
Aprender
-
Compilação e Instalação do ACE:
Saiba como compilar e instalar a biblioteca ACE.
-
Suporte ACE:
Obtenha o suporte comercial para a biblioteca ACE.
-
AIX e UNIX: A visita à zona
do developerWorks AIX e UNIX oferece fartas informações relacionadas a todos os
aspectos da administração de sistemas AIX e à expansão de suas qualificações em UNIX.
-
Novo no AIX e UNIX?
Visite a página Novo no AIX e UNIX para saber mais.
-
Livraria de
tecnologia: Pesquise por livros sobre este e outros tópicos técnicos.
Obter produtos e tecnologias
-
Estrutura ACE:
Efetue o download da estrutura de biblioteca ACE.
Discutir
-
blogs developerWorks: Verifique
os blogs de developerWorks e faça parte da
comunidade do
developerWorks.
-
Participe dos fóruns sobre AIX e UNIX:
- Fórum AIX
- Fórum AIX para desenvolvedores
- Gerenciamento de Sistemas de Cluster
- IBM Support Assistant Forum
- Fórum de Ferramentas de Desempenho
- Fórum de Virtualização
- Mais Fóruns AIX e UNIX
Arpan Sen é o principal engenheiro trabalhando no desenvolvimento de software no segmento de automação de projeto eletrônico. Ele trabalhou com diversos recursos do UNIX, incluindo o Solaris, SunOS, HP-UX e IRIX, assim como com o Linux e o Microsoft Windows por vários anos. Ele possui um grande interesse em técnicas de otimização de desempenho de software, teoria dos grafos e computação paralela. Arpan possui pós-graduação em sistemas de software. É possível entrar em contato com ele em arpan@syncad.com.