Обмен данными между потоками worker

Flash Player 11.4 и более поздних версий, Adobe AIR 13.4 и более поздних версий для ПК

Хотя потоки worker выполняют свой код в отдельных потоках выполнения, это было бы бесполезно, если бы они были полностью изолированы друг от друга. Связь между потоками worker, в конечном счете, означает обмен данными между ними. Предусмотрено три основных механизма передачи данных от одного потока worker к другому.

Выбирая метод обмена данными, подходящий для конкретной задачи, следует принять во внимание два основных пункта, в которых заключено различие этих методов. Одно из таких различий — наличие или отсутствие события для уведомления принимающего потока о доступности новых данных. При отсутствии такового поток-получатель должен сам выполнять проверку обновлений. Другое различие между этими методами обмена данными заключается в самом способе передачи данных. В одних случаях поток worker получает отдельную копию общих данных, что ведет к созданию большего числа объектов и, соответственно, к потреблению большего количества памяти и вычислительных циклов. В других случаях потоки worker работают с объектами, ссылающимися на общую системную память, что ведет к созданию меньшего числа объектов и к потреблению меньшего объема памяти в целом. Эти различия описаны в следующей таблице:

Метод передачи данных

Отправляется ли событие при получении данных?

Используется ли потоками worker общая память?

Общие свойства потоков worker

Нет

Нет, объекты копируются, не передаются по ссылкам

Объект MessageChannel

Да

Нет, объекты копируются, не передаются по ссылкам

Общий ByteArray

Нет

Да, память общая

Передача данных с помощью общего свойства

Самым элементарным способом обмена данными между потоками worker является использование общего свойства. Каждый поток worker имеет внутренний словарь значений общих свойств. Свойства хранятся со строковыми именами ключей, которые позволяют отличать одно свойство от другого. Чтобы сохранить объект в потоке worker как общее свойство, вызовите метод setSharedProperty() объекта Worker с двумя аргументами (имя ключа и значение).

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

Когда общее свойство задано, значение можно прочитать с помощью метода getSharedProperty() объекта Worker, передав имя ключа.

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

Нет ограничений относительно того, какой поток worker считывает или задает значение свойства. Например, код в фоновом потоке worker может вызвать его метод setSharedProperty() для сохранения значения. Затем код, выполняемый в родительском потоке worker, может использовать getSharedProperty() для получения данных.

Значение, передаваемое методу setSharedProperty(), может быть объектом практически любого типа. Когда вызывается метод getSharedProperty(), возвращаемый объект является копией объекта, переданного методу setSharedProperty(), а не ссылкой на сам объект, за исключением нескольких особых случаев. Обмен данными подробно рассматривается в разделе Общие ссылки и скопированные значения.

Самым значительным преимуществом использования общего свойства для обмена данными между потоками worker является то, что оно доступно еще до запуска потока worker. Можно вызвать метод setSharedProperty() фонового объекта Worker, чтобы задать общее свойство еще до запуска потока worker. Когда родительский поток worker вызывает метод start() объекта Worker, среда выполнения вызывает конструктор главного класса дочернего потока worker. Все общие свойства, заданные до вызова метода start(), становятся доступными для чтения кодом дочернего потока worker.

Передача данных с помощью объекта MessageChannel

Канал сообщений обеспечивает одностороннюю связь для передачи данных между двумя потоками worker. Использование объекта MessageChannel для передачи данных между объектами обеспечивает одно ключевое преимущество. Когда отправляется сообщение (объект) с помощью канала сообщений, объект MessageChannel отправляет событие channelMessage. Код в потоке-получателе worker может прослушивать это событие для определения доступности данных. Таким образом потоку-получателю worker не требуется постоянно проверять обновления данных.

Канал сообщений связан только с двумя потоками worker: отправителем и получателем. Чтобы создать объект MessageChannel, вызовите метод createMessageChannel() объекта-отправителя Worker, передав в качестве аргумента поток-получатель worker.

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

Оба потока worker должны иметь доступ к объекту MessageChannel. Проще всего для этого передать объект MessageChannel с помощью метода setSharedProperty().

receivingWorker.setSharedProperty("incomingChannel", sendChannel);

В потоке-получателе worker зарегистрируйте прослушиватель для события channelMessage объекта MessageChannel. Это событие отправляется, когда поток-отправитель worker передает данные через канал сообщений.

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

Чтобы передать данные, в потоке-отправителе worker вызовите метод send() объекта MessageChannel.

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

В потоке-получателе worker объект MessageChannel вызывает обработчик событий channelMessage. Затем поток-получатель worker может получить данные, вызвав метод receive() объекта.

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

Объект, возвращенный методом получения, имеет тот же тип данных, что и объект, переданный методу send(). Полученный объект является копией объекта, переданного отправителем, а не ссылкой на объект в потоке-отправителе worker, если он не относится к одному из немногих типов данных, описанных в разделе Общие ссылки и скопированные значения.

Передача данных при помощи общего массива ByteArray

При передаче объекта от одного потока worker к другому, последний получает новый объект, являющийся копией исходного. Эти два объекта хранятся в различных ячейках системной памяти. Как следствие, каждая передача объекта приводит к созданию его копии, что увеличивает общую память, потребляемую при выполнении приложения. В дополнение к этому никакие изменения, произведенные над объектом в одном потоке, не отразятся в другом потоке. Дополнительные сведения о методике копирования данных приведены в разделе Общие ссылки и скопированные значения.

По умолчанию объект ByteArray обладает таким же поведением. При передаче экземпляра ByteArray при помощи метода setSharedProperty() объекта Worker или метода send() объекта MessageChannel в памяти компьютера создается новый объект ByteArray, и принимающий поток получает ссылку на только что созданный объект. Однако поведение объекта ByteArray можно поменять, задав свойству shareable этого объекта значение true.

При передаче объекта ByteArray от одного потока worker к другому, последний получает экземпляр ByteArray, ссылающийся на ту же оперативную системную память, что и экземпляр ByteArray первого потока worker. Как только программа в одном потоке worker изменит содержимое общего байтового массива, все изменения сразу же станут доступны всем потокам, имеющим доступ к этому массиву.

Поскольку объекты worker выполняют программный код одновременно, два объекта worker потенциально могут получить доступ к одному и тому же байтовому массиву в один и тот же момент. Это может привести к потере или повреждению данных. Для предупреждения таких проблем можно использовать несколько различных средств, позволяющих управлять доступом к общим ресурсам.

Класс ByteArray имеет методы для проверки и изменения содержимого байтового массива за одну операцию:

Кроме того, пакет flash.concurrent содержит классы, предоставляющие контроль доступа при работе с общими ресурсами:

Общие ссылки и скопированные значения

В обычных случаях при вызове метода Worker.setSharedProperty() или MessageChannel.send() передача объекта потоку-получателю worker осуществляется путем его сериализации в формате AMF. Это влечет за собой несколько последствий.

  • Объект, который создается в потоке-получателе worker в результате вызова его метода getSharedProperty(), десериализируется из байт AMF. Это копия исходного объекта, а не ссылка на объект. Любые изменения, внесенные в объект в одном потоке worker, не отражаются в копии с другом потоке worker.

  • Объекты, которые нельзя сериализировать в формате AMF, например экранные объекты, нельзя передать потоку worker с помощью Worker.setSharedProperty() или MessageChannel.send().

  • Чтобы можно было правильно десериализировать пользовательский класс, определение класса должно быть зарегистрировано с помощью функции flash.net.registerClassAlias() или метаданных [RemoteClass]. Для версий класса обоих потоков worker должен использоваться один и тот же псевдоним.

Имеется пять особых объектов, которые предоставляют общий доступ к своим ресурсам, а не копируются между потоками worker:

  • объекты Worker;

  • объекты MessageChannel;

  • общий байтовый массив (объект ByteArray, у которого свойство shareable имеет значение true);

  • объекты Mutex;

  • объекты Condition.

Когда экземпляр одного из этих объектов передается с помощью метода Worker.setSharedProperty() или MessageChannel.send(), каждый поток worker имеет ссылку на один и тот же базовый объект. Изменения, внесенные в экземпляр в одном потоке worker, сразу отражаются в других потоках worker. Кроме того, если один и тот же экземпляр одного из таких объектов передается потоку worker несколько раз, среда выполнения не создает новую копию объекта в потоке-получателе worker. Вместо этого повторно используется та же ссылка.

Дополнительные приемы обмена данными

В дополнение к механизмам передачи данных, предназначенным специально для потоков worker, обмен данными между потоками worker также может осуществляться с помощью любого существующего API-интерфейса, который поддерживает обмен данными между двумя SWF-приложениями, например:

  • локальные общие объекты,

  • запись данных в файл в одном потоке worker и чтение данных из файла в другом потоке worker,

  • сохранение и чтение данных в базе данных SQLite.

Когда один ресурс используется двумя и более потоками worker, обычно рекомендуется избегать одновременного обращения нескольких потоков worker к ресурсу. Так, например, когда несколько потоков worker одновременно обращаются к файлу в локальной файловой системе, может произойти потеря или повреждение данных. Одновременный доступ может не поддерживаться операционной системой.

Во избежание проблем по причине одновременного доступа используйте классы Mutex и Condition из пакета flash.concurrent, предоставляющие контроль доступа при работе с общими ресурсами.

В отличие от других механизмов совместного использования данных, ядро базы данных SQLite предназначено для параллельного доступа и имеет собственную встроенную поддержку транзакций. Несколько потоков worker могут одновременно обращаться к базе данных SQLite без риска повреждения данных. Так как потоки worker используют разные экземпляры SQLConnection, каждый из них обращается к базе данных в рамках отдельной транзакции. Одновременные операции с данными не влияют на целостность данных.

См. также

Работа с локальными базами данных SQL в AIR

Пакет flash.concurrent