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 ,藉此變更這個行為。

當可共用的 ByteArray 物件在 Worker 之間傳遞時,接收端 Worker 中的 ByteArray 實體是傳送端 Worker 中 ByteArray 實體所使用之相同基礎作業系統記憶體的參考。當一個 Worker 中的程式碼變更位元組陣列的內容時,那些變更會立即出現在另一個可以存在該共用位元組陣列的 Worker 中。

由於多個 Worker 會同時執行程式碼,因此,可能會有兩個 Worker 同時嘗試存取位元組陣列中的相同位元組。這可能會導致資料遺失或損毀。您可以使用幾個 API 來管理共用資源的存取,並避免發生那些問題。

ByteArray 類別的方法可讓您驗證並變更單一作業中的位元組陣列內容。

此外,flash.concurrent 套件中的類別會提供可與共用資源搭配使用的存取控制:

共用的參考和複製的值

在正常情況下,當您呼叫 Worker.setSharedProperty() MessageChannel.send() 時,傳遞至接收端 Worker 的物件會透過序列化為 AMF 格式的方式來傳遞。這會產生一些後果:

  • 呼叫 getSharedProperty() 方法時在接收端 Worker 中建立的物件,會從 AMF 位元組還原序列化。它是原始物件的副本,而非物件的參考。在任一 Worker 中對物件所做的任何變更,都不會在另一個 Worker 中的副本變更。

  • 無法使用 Worker.setSharedProperty() MessageChannel.send() ,將不能序列化為 AMF 格式的物件 (例如顯示物件) 傳遞至 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 套件