ワーカー間の通信

Flash Player 11.4 以降、Adobe AIR 13.4 以降(デスクトッププラットフォーム向け)

それぞれのワーカーは個別の実行スレッド内でコードを実行しますが、ワーカーがお互いに完全に分離されているとすると何のメリットもありません。複数のワーカー間での通信は、結局のところ、ワーカー間でのデータの受け渡しを意味します。あるワーカーのデータを他のワーカーに渡すメカニズムは主に 3 つあります。

これらのデータ共有方法のいずれがデータを渡す際に適切な方法であるかを判断するときには、これらの方法に関する 2 つの主な相違点を考慮してください。第 1 の相違点は、新しいデータが使用可能になったことを受信者に通知するイベントが存在するかどうか、または受信ワーカーが更新をチェックする必要があるかどうかという点です。第 2 の相違点は、データが実際に渡される方法に関連しています。受信ワーカーが共有データのコピーを取得する場合、より多くのオブジェクトが作成され、メモリと CPU サイクルの消費量も増加します。また、基盤となる同一のシステムメモリを参照するオブジェクトにワーカーがアクセスする場合は、作成されるオブジェクトは少なくなり、全体で使用されるメモリの量も少なくなります。これらの相違点を次にまとめました。

通信方法

データを受信したときにイベントを送出する

ワーカー間でメモリを共有する

ワーカー共有プロパティ

不可

不可。オブジェクトは参照ではなく、コピーになります。

MessageChannel

不可。オブジェクトはは参照ではなく、コピーになります。

共有可能な ByteArray

不可

可。メモリは共有されます。

共有プロパティを使用したデータの受け渡し

ワーカー間でデータを共有する最も基本的な方法は、共有プロパティを使用することです。それぞれのワーカーで、共有プロパティの値を管理するための内部辞書を保持します。プロパティを区別するために、文字列型のキー名と共にプロパティを保存します。ワーカー上でオブジェクトを共有プロパティとして保存するには、Worker オブジェクトの setSharedProperty() メソッドを、保存するキー名と値という 2 つの引数を指定して呼び出します。

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

共有プロパティの設定後は、Worker オブジェクトの getSharedProperty() メソッドにキー名を渡して呼び出すことで、値を読み取ることができます。

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

プロパティ値の読み取りや設定を行うワーカーに関する制約はありません。例えば、バックグラウンドワーカーのコードで setSharedProperty() メソッドを呼び出して値を保存し、次に、親ワーカーの実行中のコードで getSharedProperty() を使用してデータを取得できます。

setSharedProperty() メソッドに渡す値では、ほぼすべてのタイプのオブジェクトが許容されます。getSharedProperty() メソッドを呼び出して返されるオブジェクトは、いくつかの特殊なケースを除き、setSharedProperty() に渡したオブジェクトのコピーとなります。同じオブジェクトに対する参照ではありません。データの共有方法に関する詳細は、共有参照とコピーされた値で説明しています。

共有プロパティを使用したワーカー間でのデータの受け渡しにおける最大のメリットは、ワーカーの実行前でも共有プロパティを使用できることです。ワーカーの実行前でも、バックグラウンド Worker オブジェクトの setSharedProperty() メソッドを呼び出して共有プロパティを設定できます。親ワーカーで Worker の start() メソッドを呼び出したときに、ランタイムによって子ワーカーのメインクラスのコンストラクターが呼び出されます。start() が呼び出される前に設定された共有プロパティはすべて、子ワーカーのコードで読み取ることができます。

MessageChannel を使用したデータの受け渡し

メッセージチャンネルは、2 つのワーカー間で単方向のデータ受け渡しリンクを確立します。MessageChannel オブジェクトを使用したワーカー間でのデータ受け渡しには、1 つの重要なメリットがあります。メッセージチャンネルを使用してメッセージ(オブジェクト)を送信する場合、MessageChannel オブジェクトによって channelMessage イベントが送出されます。受信ワーカーのコードでそのイベントをリッスンして、データを取得できるタイミングを把握できます。そのようにした場合、受信ワーカーではデータの更新状況を継続的に確認する必要がありません。

メッセージチャンネルは、送信側と受信側の 2 つのワーカーのみに関連付けられます。MessageChannel オブジェクトを作成するには、送信 Worker オブジェクトの createMessageChannel() メソッドを、受信ワーカーを引数として渡して呼び出します。

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

両方のワーカーから MessageChannel オブジェクトにアクセスできる必要があります。これを実行する最も単純な方法は、setSharedProperty() メソッドを使用して MessageChannel オブジェクトを渡すことです。

receivingWorker.setSharedProperty("incomingChannel", sendChannel);

受信ワーカーでは、MessageChannel オブジェクトの channelMessage イベントに対するリスナーを登録します。このイベントは、送信ワーカーがメッセージチャンネルを通してデータを送信するときに送出されます。

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

実際にデータを送信するには、送信ワーカーで、MessageChannel オブジェクトの send() メソッドを呼び出します。

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

受信ワーカーでは、MessageChannel で channelMessage イベントハンドラーを呼び出します。次に、受信ワーカーで、MessageChannel オブジェクトの receive() メソッドを呼び出して、データを取得できます。

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

receive メソッドから返されたオブジェクトは、send() メソッドに渡したオブジェクトと同じデータタイプになります。受信するオブジェクトは、いくつかのデータタイプのオブジェクトを除き、送信側によって渡されたオブジェクトのコピーとなります。送信ワーカー内にあるオブジェクトに対する参照ではありません。詳しくは、共有参照とコピーされた値で説明します。

共有可能な ByteArray を使用したデータの共有

オブジェクトが 2 人のワーカーの間で渡されるとき、受信ワーカーが取得する新しいオブジェクトは元のオブジェクトのコピーです。これにより、2 つのオブジェクトがシステムのメモリ内の異なる場所に格納されることになります。したがって、受信されるオブジェクトの各コピーによって、ランタイムで使用されるメモリの合計量が増加します。また、あるワーカーのオブジェクトに対して行った変更は、他のワーカーが取得したコピーには反映されません。データのコピー方法について詳しくは、共有参照とコピーされた値を参照してください。

デフォルトでは、ByteArray オブジェクトは同じ動作を使用します。Worker オブジェクトの setSharedProperty() メソッドまたは MessageChannel オブジェクトの send() メソッドに ByteArray インスタンスを渡す場合、ランタイムによってコンピューターのメモリ内に新しい ByteArray が作成され、受信ワーカーはその新しい ByteArray に対する参照である ByteArray インスタンスを取得します。ただし、ByteArray オブジェクトの shareable プロパティを true に設定することにより、ByteArray オブジェクトのこの動作を変更することができます。

共有可能な ByteArray オブジェクトが、あるワーカーから他のワーカーへ渡されるとき、受信ワーカーの ByteArray インスタンスは、送信ワーカーの ByteArray インスタンスが使用する基盤となるオペレーティングシステムメモリと同じメモリを参照しています。あるワーカーのコードでバイト配列の内容が変更されると、これらの変更は、その共有バイト配列にアクセスできる他のワーカー側でも直ちに使用できるようになります。

各ワーカーはそれぞれのコードを同時に実行できるので、2 人のワーカーがバイト配列内の同じバイトに同時にアクセスする可能性があります。これにより、データの消失や破損につながる恐れがあります。共有リソースへのアクセスを管理し、こうした問題を回避するために使用できる API がいくつかあります。

ByteArray クラスには、1 つの操作でバイト配列の内容の検証と変更を実行できる次のメソッドがあります。

また、flash.concurrent パッケージには、共有リソースを操作するためのアクセス制御を提供する次のクラスも含まれています。

共有参照とコピーされた値

通常、Worker.setSharedProperty() または MessageChannel.send() を呼び出して受信ワーカーに渡されるオブジェクトは、AMF 形式で直列化して渡されます。これには次のような重要な点があります。

  • getSharedProperty() メソッドの呼び出し時に受信ワーカー内に作成されたオブジェクトは、AMF バイトデータから直列化解除されたものです。これは、オリジナルのオブジェクトのコピーであり、オリジナルのオブジェクトに対する参照ではありません。いずれかのワーカー内のオブジェクトに対して加えられた変更は、他のワーカー内のコピー内には反映されません。

  • 表示オブジェクトなど、AMF 形式で直列化できないオブジェクトは、Worker.setSharedProperty() または MessageChannel.send() を使用してワーカーに渡すことはできません。

  • カスタムクラスを正常に直列化解除するためには、flash.net.registerClassAlias() 関数または [RemoteClass] メタデータを使用してクラス定義を登録する必要があります。両方のワーカーのクラスのバージョンで同じエイリアスを使用する必要があります。

オブジェクトがワーカー間でコピーされるのではなく、本当の意味で共有される特殊なケースが 5 つあります。

  • Worker オブジェクト

  • MessageChannel オブジェクト

  • 共有可能なバイト配列(shareable プロパティが true になっている ByteArray オブジェクト)

  • Mutex オブジェクト

  • Condition オブジェクト

Worker.setSharedProperty() メソッドまたは MessageChannel.send() メソッドを使用してこれらのいずれかのオブジェクトのインスタンスを渡すと、それぞれのワーカーには同じオリジナルのオブジェクトに対する参照が設定されます。あるワーカー内でインスタンスに変更を加えると、その内容は他のワーカーに即座に反映されます。さらに、これらのいずれかのオブジェクトのインスタンスと同じものを、1 つのワーカーに 2 回以上渡した場合、受信ワーカー内のオブジェクトの新しいコピーは作成されません。代わりに、同じ参照が再利用されます。

その他のデータ共有テクニック

データの受け渡しに関するワーカー固有のメカニズムに加えて、2 つの SWF アプリケーション間でデータを共有できる既存の API を使用して、ワーカー間でデータをやり取りすることもできます。例えば、次のような方法があります。

  • ローカル共有オブジェクト

  • あるワーカー内でファイルにデータを書き込み、別のワーカー内でそのファイルから読み取る方法

  • SQLite データベースを介してデータの保存と読み取りを行う方法

2 つ以上のワーカー間でリソースを共有する際には、通常、複数のワーカーがそのリソースに同時にアクセスできないようにする必要があります。例えば、ローカルファイルシステム上のファイルに対して複数のワーカーがアクセスすると、データの消失や破損につながる恐れがあり、この操作はオペレーティングシステムによってサポートされない可能性があります。

並行アクセスによる問題を防ぐために、flash.concurrent パッケージに含まれる Mutex および Condition クラスを使用して、共有リソースを操作する際にアクセス制御を行います。

他のデータ共有メカニズムとは異なり、SQLite データベースエンジンは並行アクセスを目的として設計されており、独自のトランザクションサポートが組み込まれています。データ破損のリスクを負うことなく、複数のワーカーが SQLite データベースにアクセスできます。ワーカーはそれぞれ異なる SQLConnection インスタンスを使用するので、各ワーカーは個別のトランザクション内でデータベースにアクセスします。そのため、同時にデータを操作することによって、データの完全性が影響を受けることはありません。

参照項目

AIR を使用したローカル SQL データベースの操作

flash.concurrent パッケージ