在 worker 之间进行通信

用于桌面平台的 Flash Player 11.4 和更高版本以及 Adobe AIR 13.4 和更高版本

虽然 worker 在单独的执行线程中执行其代码,但如果完全将其相互隔离,它们将不具有任何优势。worker 之间的通信最终意味着在 worker 之间传递数据。可使用三种主要机制在 worker 之间获取数据。

确定适合于特定数据传递需求的数据共享技术时,请考虑这些技术之间的两大差异。一项差异在于是否存在一个事件来通知接收器新数据可用或接收 worker 是否必须检查更新。数据共享技术之间的另一项差异与数据实际传递的方式相关。在某些情况下,接收 worker 获取共享数据的副本,这意味着需要创建更多对象,从而占用更多内存和 CPU 周期。在其他情况下,worker 可对引用相同系统基础内存的对象进行访问,这意味着创建的对象有所减少,整体而言减少了所使用的内存。此处对这些差异进行了概括:

通信技术

接收数据时调度事件

在 worker 之间共享内存

Worker 共享属性

否,对象是副本,不是引用

MessageChannel

否,对象是副本,不是引用

可共享 ByteArray

是,内存已共享

使用共享属性传递数据

在 worker 之间共享数据最基本的方法是使用共享属性。每个 worker 均保留共享属性值的内部词典。使用字符串键名存储属性以区分属性。要将 worker 中的对象存储为共享属性,请使用两个参数(键名和要存储的值)调用 Worker 对象的 setSharedProperty() 方法:

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

在设置了共享属性后,可通过调用 Worker 对象的 getSharedProperty() 方法来读取该值,从而传递键名:

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

worker 读取或设置属性值无限制。例如,后台 worker 中的代码可以调用其 setSharedProperty() 方法以存储某值。然后,在父 worker 中运行的代码可以使用 getSharedProperty() 以接收该数据。

传递到 setSharedProperty() 方法中的值几乎可以是任何类型的对象。除了一些特殊情况外,当调用 getSharedProperty() 方法时,所返回的对象是传递到 setSharedProperty() 的对象副本,而不是对同一对象的引用。 共享引用和复制值 中介绍了有关如何共享数据的细节。

使用共享属性在 worker 之间传递数据的最大优势是,即使在 worker 运行之前其也可用。即使在 worker 运行之前,您也可以调用后台 Worker 对象的 setSharedProperty() 方法来设置共享属性。当父 worker 调用 Worker 的 start() 方法时,运行时调用子 worker 主类的构造函数。在调用 start() 之前设置的任何共享属性均可用于读取子 worker 中的代码。

使用 MessageChannel 传递数据

消息通道可在两个 worker 之间提供单向数据传递链接。使用 MessageChannel 对象在 worker 之间传递数据具有一个主要优势。当使用消息通道发送消息(对象)时,MessageChannel 对象将调度 channelMessage 事件。接收 worker 中的代码可以侦听该事件以了解数据何时可用。这样,接收 worker 不需要持续检查数据更新。

消息通道仅与两个 worker 相关联,即发送器和接收器。要创建 MessageChannel 对象,请调用发送 Worker 对象的 createMessageChannel() 方法,将接收 worker 作为参数进行传递:

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

两个 worker 均需有访问 MessageChannel 对象的权限。做这件事最简单的方法便是使用 setSharedProperty() 方法传递 MessageChannel 对象。

receivingWorker.setSharedProperty("incomingChannel", sendChannel);

在接收 worker 中,为 MessageChannel 对象的 channelMessage 事件注册一个侦听器。当发送 worker 通过消息通道发送数据时,将会对该事件进行调度。

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

要实际发送数据,请在发送 worker 中调用 MessageChannel 对象的 send() 方法:

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

在接收 worker 中,MessageChannel 调用 channelMessage 事件处理函数。然后,接收 worker 可以通过调用 MessageChannel 对象的 receive() 方法来获取数据。

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

接收方法所返回的对象与传递到 send() 方法的对象具有相同的数据类型。接收到的对象是发送器所传入对象的副本而不是对发送 worker 中对象的引用,除非其是 共享引用和复制值 中所述的一些数据类型之一。

使用可共享 ByteArray 共享数据

在两个 worker 之间传递对象时,接收 worker 获得新对象,该对象是原始对象的副本。两个对象存储在系统内存中的不同位置。因此,接收到的对象的每份副本增加了运行时所使用的总内存。此外,您在一个 worker 中针对对象所做的任何更改并不影响另一个 worker 中的副本。有关如何复制数据的详细信息,请参阅 共享引用和复制值

默认情况下,ByteArray 对象使用相同行为。如果您将 ByteArray 实例传递给 Worker 对象的 setSharedProperty() 方法或 MessageChannel 对象的 send() 方法,则运行时在计算机内存中创建新 ByteArray,而接收 worker 获得 ByteArray 实例(此实例是该新 ByteArray 的引用)。然而,您可以通过将 ByteArray 对象的 shareable 属性设置为 true ,更改其行为。

在 worker 之间传递可共享 ByteArray 对象时,接收 worker 中的 ByteArray 实例是发送 worker 中 ByteArray 实例所用的相同操作系统基础内存的引用。当一个 worker 中的代码更改字节数组的内容时,这些更改在访问该共享字节数组的其他 worker 中立即生效。

由于各 worker 同时执行其代码,所以可能发生两个 worker 同时尝试访问字节数组中相同字节的情况。这可能会导致数据丢失或损坏。您可以使用一些 API 对共享资源的访问进行管理,从而避免这些问题。

ByteArray 类包含多个可在单个操作中验证并更改字节数组内容的方法:

此外,flash.concurrent 包包含可提供共享资源访问控制的类。

共享引用和复制值

在正常情况下,当调用 Worker.setSharedProperty() MessageChannel.send() 时,传递到接收 worker 的对象通过以 AMF 格式序列化而进行传递。这将带来一些后果:

  • 当调用其 getSharedProperty() 方法时,在接收 worker 中创建的对象从 AMF 字节中进行反序列化。其是原始对象的副本,而不是对对象的引用。在任一 worker 中对对象所做的更改在另一 worker 的副本中不会更改。

  • 无法以 AMF 格式进行序列化的对象(如显示对象)无法使用 Worker.setSharedProperty() MessageChannel.send() 传递到 worker。

  • 为了对自定义类进行正确反序列化,必须使用 flash.net.registerClassAlias() 函数或 [RemoteClass] 元数据注册类定义。该类的两个 worker 版本必须使用相同的别名。

worker 之间真正实现对象共享而非复制的特殊情况有五种:

  • Worker 对象

  • MessageChannel 对象

  • 可共享字节数组( shareable 属性为 true 的 ByteArray 对象)

  • Mutex 对象

  • Condition 对象

当使用 Worker.setSharedProperty() 方法或 MessageChannel.send() 方法传递这些对象之一的实例时,每个 worker 均引用同一个基础对象。在一个 worker 中对实例所做的更改可立即用于另一 worker 中。此外,如果将这些对象之一的相同实例不止一次传递到 worker,运行时不会在接收 worker 中创建该对象的新副本。相反,将重新使用相同引用。

其他数据共享技巧

除了用于传递数据的特定于 worker 的机制之外,worker 还可使用支持在两个 swf 应用程序之间共享数据的任何现有 api 来交换数据,如下:

  • 本地共享对象

  • 在一个 worker 中将数据编入文件并在另一个 worker 中从该文件中读取

  • 将数据存入 SQLite 数据库并从中读取数据

当在两个或更多 worker 中共享资源时,通常需要避免让多个 worker 同时访问资源。例如,让多个 worker 访问本地文件系统中的文件时,可能导致数据丢失或损坏或者不受操作系统支持。

要防止并发访问问题,使用 flash.concurrent 包的 Mutex 类和 Condition 类,以提供共享资源访问控制。

与其他数据共享机制不同,SQLite 数据库引擎旨在用于并发访问,并具有其自身内置事务支持。多个 worker 可访问 SQLite 数据库,而不会有损坏数据的风险。由于 worker 使用不同的 SQLConnection 实例,因此每个 worker 在单独事务中访问数据库。同步数据处理操作不会影响数据的完整性。

另请参阅

在 AIR 中使用本地 SQL 数据库

flash.concurrent 包