Scala актеры управления потоком - PullRequest
3 голосов
/ 06 сентября 2010

Я хотел бы узнать больше о том, насколько возможно контролировать, где работает каждый актер Scala. Я оказался в каком-то особом случае: желательна высокая реактивность, хорошая часть кода критична по времени, и, что хуже всего, я на Android. Учитывая это, моя главная цель: сделать код максимально читабельным и понятным.

Чего бы я в идеале хотел бы достичь (некоторые из них кажутся необоснованными / поначалу глупыми, прочтите объяснения ниже)
- Я хотел бы иметь возможность отвечать на некоторые сообщения в каком-то конкретном, всегда одном и том же, произвольном потоке, но мне не нужно блокировать его для ожидания сообщений.
- Я хотел бы, чтобы большая часть моей обработки выполнялась в пуле рабочих потоков, в идеале с автоматическим изменением размера при поддержке актеров Scala, при этом гарантируя, что эта обработка никогда не будет выполняться в произвольном потоке выше.

Эти требования проистекают из необходимости Android: платформа Android использует специальный поток для прикосновения к пользовательскому интерфейсу, и если вы прикоснетесь к любому объекту пользовательского интерфейса из другого потока, вы получите исключение. Таким образом, он применяет какую-то модель потоков / блокировок, что я и пытаюсь обойти. Но в любом случае, так оно и есть: я должен убедиться, что часть моей обработки, а именно то, что связано с объектами пользовательского интерфейса, выполняется в этом потоке, а не в другом, потому что фреймворк досадно говорит, что именно так я и должен это делать. Раздражающим побочным эффектом этого является то, что пока этот поток обрабатывает мой код, пользовательский интерфейс перестает обновляться, а мое приложение перестает отвечать на запросы. Поэтому мне нужно убедиться, что этот поток не будет выбран случайным образом для продолжительного кода, который я мог бы иметь в некоторых реакциях {}, и в идеале он никогда не обрабатывает то, что могло бы быть сделано другим потоком.

Платформа Android предоставляет класс Handle, который реализует какую-то передачу сообщений - вы отправляете ему Runnable, и он будет работать в потоке пользовательского интерфейса. Я могу использовать это, если мне нужно. Создание Runnable каждый раз значительно загромождает код - единственное, что можно сделать, это заключить его в некоторый метод, чтобы я мог написать что-то вроде
onUIThread {/ * некоторый код * /}
... что намного лучше, чем новый Runnable () {def run () {}}. С другой стороны, это в основном то, что будет делать функция onUIThread, поэтому я буду создавать два замыкания, а затем мне придется разбираться с деталями выделения памяти для замыканий. Мне бы пришлось, потому что каждый раз, когда я выделяю объект, GC получает возможность запуска, а на Android это обычно пауза в 150 мс, что разрушит мой пользовательский опыт, если это произойдет в критическом пути выполнения.

Итак, в конце концов:
- Есть ли у меня какой-либо способ статически связать актера с потоком, чтобы я мог иметь актора пользовательского интерфейса, реагировать {} внутри него и всегда запускать его код в потоке пользовательского интерфейса? / * Я знаю, что это плохой дизайн сам по себе, прочитайте обоснование выше, чтобы понять, почему я не могу с этим поделать * /
- Есть ли у меня способ гарантировать, что этот конкретный поток никогда не будет рассматриваться для ответа на сообщение в реакции {}? - Какие-либо предложения о том, что я мог бы сделать, учитывая мои ограничения, чтобы улучшить читаемость кода?

1 Ответ

5 голосов
/ 06 сентября 2010

Вы можете реализовать планировщик клиента, расширив черту IScheduler, которая помещает задачи в поток пользовательского интерфейса, а затем переопределите метод планировщика на вашем акторе, который должен работать в потоке пользовательского интерфейса.

Кто-то пробовал это с Swing некоторое время назад, и я думаю, что это сработало: http://scala -programming-language.1934581.n4.nabble.com / Scala-свинг-событийно-нить-актеры-td1987246.html

Тогда пусть другие актеры используют обычный планировщик.

Но я укажу, что реакция {} приводит к созданию замыкания, которое в конечном итоге оборачивается в работоспособный объект. Он также использует исключения для управления потоком, что приводит к значительным накладным расходам (я понятия не имею, сколько на Dalvik). Поэтому, если закрытие GC действительно ухудшает производительность вашего приложения, у меня есть сомнения, что актеры спасут вас.

...