Comunicação entre workers

Flash Player 11.4 e posterior, Adobe AIR 13.4 e posterior para plataformas desktop

Apesar de workers executarem seus códigos em cadeias de execução separadas, eles não ofereceriam qualquer benefício se estivessem completamente isolados uns dos outros. Em resumo, a comunicação entre workers significa transmitir dados entre workers. Existem três mecanismos principais para levar dados de um worker para o outro.

Ao decidir qual dessas técnicas de compartilhamento de dados é apropriada para uma necessidade específica de transmissão de dados, considere as duas principais diferenças entre elas. Uma diferença entre elas é se há um evento para notificar o destinatário sobre a disponibilidade de novos dados ou se ele deve verificar se há atualizações. Outra diferença entre essas técnicas de compartilhamento de dados está relacionada à maneira com a qual os dados são transmitidos. Em alguns casos, o worker destinatário recebe uma cópia dos dados compartilhados, o que significa que mais objetos são criados, ocupando mais memórias e ciclos da CPU. Em outros casos, eles acessam os objetos que fazem referência à memória do mesmo sistema subjacente, o que significa que menos objetos são criados e, de maneira geral, menos memória é utilizada. Essas diferenças são descritas aqui:

Técnica de comunicação

Envia um evento ao receber dados

Compartilha memória entre workers

Propriedades compartilhadas entre workers

Não

Não, os objetos são cópias, não referências

MessageChannel

Sim

Não, os objetos são cópias, não referências

ByteArray compartilhável

Não

Sim, a memória é compartilhada

Transmitir dados com uma propriedade compartilhada

O modo mais básico de compartilhar dados entre workers é utilizando uma propriedade compartilhada. Cada worker mantém um dicionário interno de valores de propriedade compartilhados. As propriedades são armazenadas com uma nomes chave de sequência para fins de distinção entre elas. Para armazenar um objeto em um worker como uma propriedade compartilhada, chame o método setSharedProperty() do objeto do worker com dois argumentos, o nome chave e o valor a armazenar:

// code running in the parent worker 
bgWorker.setSharedProperty("sharedPropertyName", someObject);

Uma vez que a propriedade compartilhada esteja definida, o valor poderá ser lido chamando o método getSharedProperty() do objeto do worker, transferindo o nome chave:

// code running in the background worker 
receivedProperty = Worker.current.getSharedProperty("sharedPropertyName");

Não há restrição a respeito de qual worker lê ou define o valor da propriedade. Por exemplo, o código em um worker de segundo plano pode chamar o método setSharedProperty() para armazenar um valor. O código executado em um worker pai pode usar getSharedProperty() para receber os dados.

O valor transferido para o método setSharedProperty() pode ser praticamente qualquer tipo de objeto. Ao chamar o método getSharedProperty() , o objeto que retorna é uma cópia do objeto transferido para setSharedProperty() e não uma referência para o mesmo objeto, exceto em alguns casos especiais. Os princípios de como dados são compartilhados são explicados em Referências compartilhadas e valores copiados .

A maior vantagem de utilizar uma propriedade compartilhada para transferir dados entre workers é a disponibilidade mesmo antes do worker ser executado. Você pode chamar um método setSharedProperty() do objeto do worker de segundo plano para definir uma propriedade compartilhada mesmo antes da execução do worker. Quando o worker pai chama o método start() do worker, o runtime chama o construtor da classe principal do worker filho. Quaisquer propriedades compartilhadas definidas antes de chamar start() estarão disponíveis para código no worker filho.

Transferindo dados com um MessageChannel

Um canal de mensagens fornece um link unidirecional de transferência de dados entre dois workers. Usar um objeto MessageChannel para transferir dados entre workers tem uma vantagem fundamental. Quando você envia uma mensagem (um objeto) utilizando um canal de mensagem, o objeto MessageChannel despacha um evento channelMessage . O código no worker de destino ouvir este evento para saber quando os dados estão disponíveis. Dessa forma, o worker de destino não precisa verificar continuamente se há atualizações de dados.

Um canal de mensagem é associado a apenas dois workers, um de origem e um de destino. Para criar um objeto MessageChannel, chame o método createMessageChannel() do objeto do worker de origem, transmitindo o worker de destino como um argumento:

// In the sending worker swf 
var sendChannel:MessageChannel; 
sendChannel = Worker.current.createMessageChannel(receivingWorker);

Ambos workers precisam ter acesso ao objeto MessageChannel. O modo mais simples de fazer isso é transferindo o objeto MessageChannel usando o método setSharedProperty() :

receivingWorker.setSharedProperty("incomingChannel", sendChannel);

No worker de destino, registre um ouvinte para o evento channelMessage do objeto MessageChannel. Esse evento é despachado quando o worker de origem envia dados pelo canal de mensagens.

// In the receiving worker swf 
var incomingChannel:MessageChannel; 
incomingChannel = Worker.current.getSharedProperty("incomingChannel"); 
incomingChannel.addEventListener(Event.CHANNEL_MESSAGE, handleIncomingMessage);

Para realmente enviar dados, chame o método send() do objeto MessageChannel no worker de origem:

// In the sending worker swf 
sendChannel.send("This is a message");

No worker de destino, o MessageChannel chama o manipulador de eventos channelMessage . O worker de destino pode obter os dados chamando o método receive() do objeto MessageChannel.

private function handleIncomingMessage(event:Event):void 
{ 
    var message:String = incomingChannel.receive() as String; 
}

O objeto retornado pelo método de destino possui o mesmo tipo de dados do objeto que foi enviado para o método send() . O objeto de destino é uma cópia do objeto enviado pelo remetente e não uma referência para o objeto no worker de origem, a menos que seja um de alguns tipos de dados, como descrito em Referências compartilhadas e valores copiados .

Compartilhamento de dados usando um ByteArray compartilhável

Quando um objeto é transmitido entre dois workers, o worker destinatário recebe um novo objeto que é uma cópia do original. Os dois objetos são armazenados em locais diferentes da memória do sistema. Consequentemente, cada cópia do objeto recebida aumenta o total de memória usado pelo runtime. Além disso, qualquer alteração realizada em um objeto de um worker não afeta a cópia do outro worker. Para obter mais detalhes sobre como os dados são copiados, consulte Referências compartilhadas e valores copiados .

Por padrão, um objeto ByteArray usa o mesmo comportamento. Se você transmitir uma instância do ByteArray para o método setSharedProperty() do objeto de um worker ou um método send() de um objeto MessageChannel, o runtime cria um novo ByteArray na memória do computador e o worker destinatário recebe uma instância do ByteArray que é uma referência ao novo ByteArray. Entretanto, você pode alterar esse comportamento de um objeto ByteArray definindo sua propriedade shareable como true .

Quando um objeto ByteArray compartilhável é transmitido entre workers, a instância do ByteArray do worker destinatário é uma referência à memória do mesmo sistema operacional subjacente usada pela instância do ByteArray no worker remetente. Quando o código de um worker altera o conteúdo da matriz de bytes, essas alterações se tornam imediatamente disponíveis em outros workers com acesso à matriz de bytes compartilhada.

Como os workers executam seu código simultaneamente, dois workers podem tentar acessar os mesmos bytes em uma matriz de bytes ao mesmo tempo. Isso pode causar perda ou corrupção dos dados. Diversas APIs podem ser usadas para gerenciar o acesso aos recursos compartilhados e evitar esse tipo de problema.

A classe ByteArray tem métodos que permitem que você valide e altere o conteúdo da matriz de bytes em uma só operação:

Além disso, o pacote flash.concurrent inclui classes que fornecem controle de acesso para trabalhar com recursos compartilhados:

Referências compartilhadas e valores copiados

No caso normal, quando você chama o Worker.setSharedProperty() ou o MessageChannel.send() ,o objeto que é transmitido ao worker de destino é serializado no formato AMF. Isso tem algumas consequências:

  • O objeto que é criado no worker de origem quando o método getSharedProperty() é chamado é desserializado pelos bytes AMF. É uma cópia do objeto original e não uma referência ao objeto. Quaisquer alterações feitas ao objeto nos workers não serão alteradas na cópia que está no outro worker.

  • Objetos que não podem ser serializados no formato AMF, como objetos de exibição, não podem ser transmitidos a um worker usando Worker.setSharedProperty() ou MessageChannel.send() .

  • Para desserializar corretamente uma classe personalizada, a definição da classe deve ser registrada utilizando a função flash.net.registerClassAlias() ou metadados [RemoteClass] . O mesmo alias deve ser usado pelas versões da classe de ambos os workers.

Há cinco casos especiais de objetos que são realmente compartilhados em vez de copiados entre workers:

  • Objetos Worker

  • Objetos MessageChannel

  • matriz de bytes compartilhável (um objeto ByteArray cuja propriedade shareable é true )

  • Objetos Mutex

  • Objetos Condition

Quando você transmite uma instância de um desses objetos utilizando o método Worker.setSharedProperty() ou o método MessageChannel.send() , cada worker possui uma referência para o mesmo objeto subjacente. Modificações feitas a uma instância em uma worker são imediatamente disponibilizadas em outros workers. Além disso, se você transmite a mesma instância de um desses objetos para um worker mais de uma vez, o runtime não cria uma nova cópia de objetos no worker de destino. Em vez disso, a mesma referência é reutilizada.

Técnicas adicionais de compartilhamento de dados

Além do mecanismo específico de workers para transmissão de dados, os workers também podem trocar dados usando as APIs existentes que suportam compartilhamento de dados entre dois aplicativos swf, como o seguinte:

  • objetos compartilhados locais

  • escrever dados em um arquivo em um worker e ler do arquivo em outro worker

  • armazenar e ler os dados de um banco de dados SQLite

Quando você compartilha um recurso entre dois ou mais workers, geralmente você precisa evitar ter vários workers acessando o recurso ao mesmo tempo. Por exemplo, vários workers acessando um arquivo do sistema de arquivos local pode causar perda ou corrupção de dados e pode não ser suportado pelo sistema operacional.

Para se proteger contra problemas de acesso simultâneo, use as classes Mutex e Condition no pacote flash.concurrent para fornecer controle de acesso para trabalhar com recursos compartilhados.

Diferentemente de outros mecanismos de compartilhamento de dados, o mecanismo do banco de dados SQLite é projetado para acesso simultâneo e possui seu próprio suporte de transação incorporado. Vários workers podem acessar um banco de dados SQLite sem o risco de corromper os dados. Como os workers usam instâncias diferentes do SQLConnection, cada worker acessa o banco de dados em uma transação separada. Operações de manipulação de dados simultâneas não afetam a integridade dos dados.

Consulte também

Trabalho com bancos de dados SQL locais no AIR

Pacote flash.concurrent