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:
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
|
|
|