Comunicazione tra worker

Flash Player 11.4 e versioni successive, Adobe AIR 13.4 e versioni successive per le piattaforme desktop

Nonostante i worker eseguano il loro codice in thread di esecuzione separati, essi non sarebbero in grado di offrire alcun vantaggio se fossero completamente isolati gli uni dagli altri. La comunicazione tra worker implica in ultima analisi il passaggio dei dati tra worker. Esistono tre meccanismi principali per trasferire dati da un worker a un altro.

Quando dovete decidere quale di queste tecniche di condivisione dei dati è la più appropriata per una particolare operazione di passaggio di dati, valutate le differenze principali tra i diversi metodi. Una differenza riguarda la presenza o meno di un evento che notifica al destinatario che sono disponibili nuovi dati oppure, al contrario, la necessità per il destinatario di verificare se sono presenti dati aggiornati. Un'altra differenza tra queste tecniche di condivisione dei dati ha a che fare con il modo in cui i dati vengono effettivamente passati. In alcuni casi il worker di destinazione riceve una copia dei file condivisi, il che significa che vengono creati altri oggetti con un conseguente consumo ulteriore di memoria e di cicli della CPU. In altri casi i worker accedono agli oggetti che fanno riferimento alla stessa memoria sottostante, per cui vengono creati meno oggetti e l'uso complessivo della memoria è inferiore. Queste differenze sono delineate di seguito:

Tecnica di comunicazione

Invia l'evento alla ricezione dei dati

Condivide la memoria tra i worker

Proprietà condivise dei worker

No

No, gli oggetti sono copie e non riferimenti

MessageChannel

No, gli oggetti sono copie e non riferimenti

ByteArray condivisibile (Shareable)

No

Sì, la memoria è condivisa

Passaggio di dati con una proprietà condivisa

Il modo più semplice per condividere dati tra più worker è utilizzare una proprietà condivisa. Ogni worker gestisce un proprio dizionario interno di valori di proprietà condivise. Le proprietà sono memorizzate con nomi chiave String per distinguerle tra loro. Per memorizzare un oggetto in un worker come proprietà condivisa, chiamate il metodo setSharedProperty() dell'oggetto Worker con due argomenti, il nome chiave e il valore da memorizzare:

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

Una volta che la proprietà condivisa è stata impostata, il valore può essere letto chiamando il metodo getSharedProperty() dell'oggetto Worker e trasmettendo il nome chiave:

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

Non vi sono restrizioni relativamente ai worker che possono leggere o definire il valore delle proprietà. Ad esempio, il codice di un worker di background può chiamare il relativo metodo setSharedProperty() per memorizzare un valore. Il codice in esecuzione nel worker principale può quindi usare getSharedProperty() per ricevere i dati.

Il valore che viene trasmesso al metodo setSharedProperty() può corrispondere praticamente a qualsiasi tipo di oggetto. Quando chiamate il metodo getSharedProperty() , l'oggetto che viene restituito è una copia dell'oggetto passato a setSharedProperty() e non un riferimento all'oggetto stesso, eccetto in alcuni casi particolari. I dettagli di come i dati vengono condivisi sono illustrati alla sezione Riferimenti condivisi e valori copiati .

Il principale vantaggio di utilizzare una proprietà condivisa per passare dati tra più worker è che la proprietà è disponibile ancor prima che il worker venga eseguito. Potete chiamare un metodo setSharedProperty() di un oggetto Worker di background per impostare una proprietà condivisa anche prima che il worker venga eseguito. Quando il worker principale chiama il metodo start() dell'oggetto Worker, il runtime chiama la funzione di costruzione della classe principale del worker secondario. Tutte le proprietà condivise impostate prima della chiamata al metodo start() sono disponibili per la lettura del codice nel worker secondario.

Passaggio di dati con un MessageChannel

Un canale messaggi offre un collegamento a senso unico per il passaggio di dati tra due worker. L'uso di un oggetto MessageChannel per passare dati da un worker a un altro offre un vantaggio importante. Quando inviate un messaggio (un oggetto) usando un canale messaggi, l'oggetto MessageChannel consegna un evento channelMessage . Il codice del worker ricevente può rimanere in ascolto dell'evento per conoscere quando i dati sono disponibili. In questo modo, il worker ricevente non ha bisogno di verificare continuamente la disponibilità di dati aggiornati.

Un canale messaggi può essere associato solo a due worker, un mittente e un ricevente. Per creare un oggetto MessageChannel, chiamate il metodo createMessageChannel() dell'oggetto Worker mittente, passando il worker ricevente come argomento:

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

Entrambi i worker devono poter accedere all'oggetto MessageChannel. Il modo più semplice per consentire tale accesso è passare l'oggetto MessageChannel usando il metodo setSharedProperty() :

receivingWorker.setSharedProperty("incomingChannel", sendChannel);

Nel worker ricevente, registrate un listener per l'evento channelMessage dell'oggetto MessageChannel. Questo evento viene consegnato quando il worker mittente invia dati attraverso il canale messaggi.

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

Per inviare effettivamente i dati, chiamate il metodo send() dell'oggetto MessageChannel nel worker mittente:

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

Nel worker ricevente, l'oggetto MessageChannel chiama il gestore dell'evento channelMessage . Il worker ricevente può a questo punto ricevere i dati chiamando il metodo receive() dell'oggetto MessageChannel.

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

L'oggetto restituito dal metodo receive presenta lo stesso tipo di dati dell'oggetto passato mediante il metodo send() . L'oggetto ricevuto è una copia dell'oggetto passato dal worker mittente e non un riferimento all'oggetto presente nel worker mittente, a meno che non si tratti di uno dei tipi di dati descritti alla sezione Riferimenti condivisi e valori copiati .

Condivisione dei dati mediante un ByteArray condivisibile

Quando un oggetto viene passato tra due worker, il worker ricevente riceve un nuovo oggetto che è una copia di quello originale. I due oggetti vengono archiviati in posizioni diverse nella memoria del sistema. Di conseguenza, ogni copia dell'oggetto che viene ricevuta va a incrementare la memoria totale utilizzata dal runtime. Inoltre, eventuali modifiche apportate a un oggetto in un worker non hanno effetto sulla copia nell'altro worker. Per altre informazioni su come vengono copiati i dati, vedete Riferimenti condivisi e valori copiati .

Per impostazione predefinita, un oggetto ByteArray usa lo stesso comportamento. Se passate un'istanza ByteArray al metodo setSharedProperty() di un oggetto Worker o al metodo send() di un oggetto MessageChannel, il runtime crea un nuovo ByteArray nella memoria del computer e il worker ricevente riceve un'istanza ByteArray che è un riferimento al nuovo ByteArray. Tuttavia, potete cambiare questo comportamento per un oggetto ByteArray impostandone la proprietà shareable su true .

Quando un oggetto ByteArray shareable (condivisibile) viene passato da un worker a un altro, l'istanza ByteArray nel worker ricevente è un riferimento alla stessa memoria sottostante del sistema operativo che è utilizzata dall'istanza ByteArray del worker mittente. Quando il codice di un worker modifica il contenuto del byte array, le modifiche sono immediatamente disponibili negli altri worker che hanno accesso al byte array condiviso.

Poiché i worker eseguono il proprio codice simultaneamente, è possibile che due worker tentino di accedere agli stessi byte di un byte array nello stesso momento. In questo caso si può verificare una perdita o un danneggiamento dei dati. Vi sono varie API che è possibile utilizzare per accedere alle risorse condivise ed evitare altri problemi.

La classe ByteArray ha dei metodi che consentono di convalidare e modificare il contenuto del byte array in un'unica operazione:

Inoltre, il pacchetto flash.concurrent include delle classi che forniscono il controllo degli accessi per lavorare con risorse condivise:

Riferimenti condivisi e valori copiati

In casi normali, quando chiamate Worker.setSharedProperty() o MessageChannel.send() , l'oggetto che viene passato al worker ricevente viene trasmesso serializzato in formato AMF. Ciò comporta quanto segue:

  • L'oggetto che viene creato nel worker ricevente quando il relativo metodo getSharedProperty() viene chiamato, viene deserializzato dai byte del file AMF. Si tratta di una copia dell'oggetto originale e non di un riferimento all'oggetto stesso. Eventuali modifiche apportate all'oggetto in uno dei due worker non vengono rispecchiate nella copia dell'altro worker.

  • Gli oggetti che non possono essere serializzati in formato AMF, quali gli oggetti di visualizzazione, non possono essere passati a un worker usando Worker.setSharedProperty() o MessageChannel.send() .

  • Perché una classe personalizzata possa essere deserializzata correttamente, la definizione della classe deve essere registrata usando la funzione flash.net.registerClassAlias() o i metadati [RemoteClass] . Lo stesso alias deve essere usato per entrambe le versioni dei worker della classe.

Vi sono cinque casi particolari di oggetti che vengono effettivamente condivisi, anziché copiati, tra worker:

  • Oggetti worker

  • Oggetti MessageChannel

  • ByteArray condivisibile (un oggetto ByteArray con la proprietà shareable impostata su true )

  • Oggetti Mutex

  • Oggetti Condition

Quando passate un'istanza di uno di questi oggetti utilizzando il metodo Worker.setSharedProperty() o il metodo MessageChannel.send() , ciascun worker conterrà un riferimento allo stesso oggetto sottostante. Le modifiche apportate a un'istanza in uno dei worker risulteranno immediatamente disponibili anche negli altri worker. Inoltre, se trasmettete la stessa istanza di uno di questi oggetti a un worker più di una volta, il runtime non creerà una nuova copia dell'oggetto nel worker ricevente, ma verrà riutilizzato lo stesso riferimento.

Altre tecniche di condivisione dei dati

Oltre ai meccanismi di passaggio dei dati specifici dei worker, i worker possono scambiarsi dati anche usando una qualsiasi delle API esistenti che supportano la condivisione di dati tra due applicazioni swf, come le seguenti:

  • Uso di oggetti condivisi locali

  • Scrittura di dati su un file in un worker e lettura dei dati dal file nell'altro worker

  • Memorizzazione di dati in un database SQLite e lettura dei dati dallo stesso database

Quando condividete una risorsa tra due o più worker, generalmente dovete evitare che più worker accedano alla risorsa contemporaneamente. Ad esempio, l'accesso da parte di più worker a un file sul file system locale potrebbe causare la perdita o il danneggiamento di dati e potrebbe non essere supportato dal sistema operativo.

Per evitare problemi di accesso contemporaneo, utilizzate le classi Mutex e Condition del pacchetto flash.concurrent per fornire il controllo degli accessi per le operazioni con le risorse condivise.

A differenza di altri meccanismi di condivisione dei dati, il database SQLite è stato progettato per l'accesso simultaneo e presenta un proprio supporto alle transazioni integrato. Più worker possono accedere a un database SQLite senza rischiare di danneggiare i dati. Poiché i worker impiegano istanze SQLConnection differenti, ciascun worker accede al database in una transazione separata. In questo modo, operazioni di manipolazione simultanea dei dati non interferiscono sull'integrità dei dati stessi.

Vedere anche

Utilizzo dei database SQL locali in AIR

Pacchetto flash.concurrent