PDA

Просмотр полной версии : Многопоточность и асинхронный вызов методов во Flex


LexeY4eg
07.04.2008, 22:23
Всем привет!

Ктонибудь может подсказать по теме? Быть может интересная литература или ещё какието ресурсы.

Столкнулся с тем, что во Flex зачастую не все работает так, как ты этого ожидаеш. Timer я так понял не запускается в отдельном потоке, и обработчики событий вызываются не всегда по цепочке. Очень интересно былобы узнать когда имеет место асинхронность и может быть есть пути делать какието вещи синхронно, если это нужно.

WindWalker
08.04.2008, 02:20
Стандартных средств для синхронного вызова методов нет.
Приходиться делать самодельные очереди.
Реализовать можно по-разному.
Например: массив "операция" (или функции, или специальные объекты) и индекс, какая из операций текущая.
Каждая операция должна сообщить о своём завершении. При этом индекс увеличится на единицу и будет выполнена запущена следующая операция.

Вариант: вместо изменения индекса просто удалять "отстрелявшуюся" операцию из очереди.

----
А вообще, как я уже неоднократно говорил ранее, надо менять компилятор, чтобы всё это было прозрачно.

etc
08.04.2008, 10:45
LexeY4eg, ActionScript сам по себе работает в единственном потоке.

LexeY4eg
08.04.2008, 13:28
У меня tcnm ytcrjkmrj вопросов:

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


for (var i:Number = 0; i < 100; i ++) {
arrayOfSomeObjects[i].someFunctio();
}


По такому ли принципу, построен вызов методов у классов-listenerов внутри EventDispatcher, или там используются какието нативные для языка вещи?



2. Если ActionScript работает в одном потоке, то как реализован Timer?



3. Где можно посмотреть исходники EventDispatcher и других классов Flex SDK, хотябы Timer ещё.

WindWalker
08.04.2008, 15:37
LexeY4eg, ActionScript сам по себе работает в единственном потоке.
Не надо путать. Flash Player работает в одном потоке.
Это не мешает коду на ActionScript быть многопоточным.

Аналогии:
JVM (по крайней мере некоторые из её реализаций) работают в одном потоке. Это не мешает коду на Java быть многопоточным.

Одноядерный процессор без Hyper Threading может исполнять только один поток одновременно. Это не мешает приложениям быть многопоточными.

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

Кроме того, один поток может быть остановлен, ожидая какого-то события. В это время может выполняться другой поток.

Разумеется, есть некоторые ограничения, накладываемые всё той же однопоточностью AVM2, например, многопоточность будет невытесняющая: каждый поток сам должен время от времени останавливать себя, давая возможность выполниться другим потокам.
То есть как в старом добром Windows 3.1

1. если я в цикле вызываю какойто метод у кучи обьектов, есть ли гарантия, что каждый последующий метод будет выполнен по завершению предыдущего?
Да. В каждую единицу времени выполняется только один метод.
Следующий будет вызван только после того, как завершиться предыдущий.
Но если эти методы запускают какие-то "долгоиграющие" действия, например: запуск анимации, проигрывание звука, запрос к серверу и т.д., то методы будут исполнятся не дожидаясь, пока эти длительные действия завершаться.

etc
08.04.2008, 15:53
WindWalker, тогда напишите компилятор, который создаст AVM2-совместимый код и реализующий эту самую многопоточность. А пока этого нет, никакой многопоточности в плеере и AS нет.

FlexOkeks
08.04.2008, 16:59
Не надо путать. Flash Player работает в одном потоке.
Это не мешает коду на ActionScript быть многопоточным.
Это что-то типа псевдомногопоточности, типа как по докумментам задания расчитаны на 10 сотрудников и зарплата на 10 человек выделяется а работает на самом деле один за десятерых, которому иногда помогают знакомые, ну там на сервер с запросом сбегать или анимацию прокрутить :quiet:

WindWalker
08.04.2008, 17:07
Да есть там всё.
Вот примерчик когда-то делал, именно как демонстрацию многопоточности.

Одновременно происходят вычисления, которым требуется очень много времени и которые в обычном случае подвесили бы флеш-плеер, и появилась бы ошибка "скрипт выполняется слишком долго".

Здесь же этого не происходит, вычисления выполняются постепенно и на экран выдаётся текущий прогрес.
По сути - два отдельных потока: один вычисляет, другой отображает.

Есть в этом что-то чудесное и удивительное? Нет. Все прекрасно знают, как это сделать, и сами делали не раз.

Но это требует написание дополнительного кода, а также рестуктуризацию самого кода вычислений (простой цикл заменяется на несколько функций).

Новый компилятор нужен только для того, чтобы освободить от этой рутины, чтобы каждый раз не изобретать этот велосипед.

Вобщем, скажем так:
AVM2 выполняется в одном потоке.
Код на abc-байткоде может быть многопоточным.
ActionScript3 не имеет встроенных средств для создания и работы с потоками, поэтому приходиться использовать обходные пути.

LexeY4eg
08.04.2008, 17:08
Но если эти методы запускают какие-то "долгоиграющие" действия, например: запуск анимации, проигрывание звука, запрос к серверу и т.д., то методы будут исполнятся не дожидаясь, пока эти длительные действия завершаться.


я написал небольшой тест:



public class Test
{

private var name:String;

public function Test(s:String) {
name = s;
}

public function test():void {
trace("start " + name);
for (var i:Number = 0; i < 1000000; i ++) {
for (var j:Number = 0; j < 100; j ++) {

}
}
trace("finish " + name);
}

}



for (var i:Number = 0; i < 3; i ++) {
var t:Test = new Test(i.toString());
t.test();
}


полученный трэйс:

start 0
finish 0
start 1
finish 1
start 2
finish 2

как вы это обьясните?

WindWalker
08.04.2008, 17:13
как вы это обьясните?
Под "долгоиграющим" действием я подразумевал не большой-пребольшой цикл, а что-то, что может быть закончено только в следующем кадре или через несколько кадров.
Нпример, загрузка внешнего файла или HTTP-запрос.

Пока цикл не закончится, функция разумеется не завершится.

etc
08.04.2008, 20:08
WindWalker, swf без сорсов ничего такого своим внешним видом не рассказывает. Если вы реализовали несколько конвееров (которые имеют псевдо одно и то же время старта), то это не многопоточность.

miramax
08.04.2008, 21:14
3. Где можно посмотреть исходники EventDispatcher и других классов Flex SDK, хотябы Timer ещё.
Исходников корных классов флешплеера (пакет flash.*) на AS3 не существует. SDK их ниоткуда не компилирует. Флешплеер предоставляет уже готовые интерфейсы, реализации которых написаны на других языках разработчиками самого плеера.
2. Если ActionScript работает в одном потоке, то как реализован Timer?
Считайте что в реализации некоторых корных классов (Timer, Loader, Stream) используются инкапсулированные потоки исполнения.

WindWalker
08.04.2008, 23:56
WindWalker, swf без сорсов ничего такого своим внешним видом не рассказывает.
Да так ли важно, как это устроено? Главное - как это выглядит для конечного пользователя.
А выглядит это просто - достаточно объёмные вычисления (9! = 362880 итераций - для AS2 это достаточно много) не подвешивают плеер, а выполняются постепенно с отображением текущего прогресса.

Принцип работы простой: есть метод, который выполняет одну-единственную итерацию.
В onEnterFrame засекается время через getTimer(), затем начинает в цикле вызываться метод и после каждого заверешения проверяется, сколько времени прошло. Метод вызывается до тех пор, пока время не превысит 20 миллисекунд. На этом обработка onEnterFrame заканчивается.
Продолжение - в следующем кадре.

То есть в каждом кадре выполняется столько итераций, сколько успеет выполниться за 20 мс.

Основная проблема такого подхода - необходимо вычленить эту самую "одну итерацию".

Если вы реализовали несколько конвееров (которые имеют псевдо одно и то же время старта), то это не многопоточность.
Ну тогда вопросы:
1. А что тогда такое многопоточность? Критерии, пожалуста.
2. Зачем нужна полная и настоящая многопоточность для синхронных вызовов?

etc
09.04.2008, 00:08
1. Независимость потоков друг от друга. Цикл на миллион итераций, выполняющийся 100 миллисекунд, никак не влияет на тот же таймер, «тикающий» каждые 10 мс;
2. А как вы сделаете два вызова в один момент времени без многопоточности?

WindWalker
09.04.2008, 00:22
1. Независимость потоков друг от друга. Цикл на миллион итераций, выполняющийся 100 миллисекунд, никак не влияет на тот же таймер, «тикающий» каждые 10 мс;
Согласен, с этим во флешплеере могут быть проблемы.
Но это условие и не обязательно. В Windows 3.1 форматирование дискетки останавливало все остальные процессы - тут уж ничего не поделаешь, недостатки невытесняющей многозадачности.
Но задача-то состоит не в том, чтобы потоки выполнялись плавно.
Основная задача - синхронные вызовы.

2. А как вы сделаете два вызова в один момент времени без многопоточности?
А тут вполне достаточно квазимногопоточности, неполноценной многопоточности, многоконвеерности на худой конец.
Гарантии, что таймер будет тикать раз в 10мс, а fps будет всегда ровно 30, нам тут совершенно не требуются.

miramax
09.04.2008, 02:21
2WindWalker
Суть потока в том, что когда мы пишем код исполняемых им функций мы абстрагируемся от распределения процессорного времени.
А здесь вы предлагаете в каждом вызове функции, проверять таймер и писать ещё дофига кода для обрывания "потока" исполнения и запоминания состояния, получается код во время своего исполнения занимается постоянным мониторингом псевдопотока.
бууууэээээ....

WindWalker
09.04.2008, 04:10
Нет, я вовсе не предлагаю проверять таймер.
Это просто одна из реализаций - эксперимент на тему "хочу квантование".

Но вы правы - действительно для того, чтобы сделать что-то потокообразное - либо для синхронных вызовов, либо (уже другая, но смежная задача) для более плавного выполнения больших вычислений/парсинга xml/обработки битмапов/прочего - необходимо написать оочень много лишнего кода. Более того - код должен иметь особую структуру. Каждая возможная пауза означает создание как минимум одной новой функции.
И иногда это может повернуться боком.

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

-------------------
А вообще разговоры про многопоточность меня уже достали.

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

Но все почему-то упорно зацикливаются на всякой маловажной ерунде.

etc
09.04.2008, 09:40
Куда-нибудь, в светлом будущем, она может быть будет…

WindWalker
10.04.2008, 03:56
Итак, постановка задачи...

Очень часто требуется выполнить несколько действий последовательно.
Каждое последущее должно начинать выполняться только после завершения предыдущего.

Примеры:

1. Установить сокетное соединение с сервером. Только после того, как соединение установленно, отправить hand-shake запрос. Только после того, как hand-shake подтверждён, начинать дальнейший сетевой обмен. (Вариация: сперва соединиться с login-сервером, отправить имя/пароль, получить id сессии, соединиться с game-сервером, отправить id сессии, получить список комнат, войти в комнату по умолчанию).

2. Загрузить текстуру. Только после того, как текстура загружена, наложить её на объект.

3. Показать пользователю диалоговое окно с кнопкой OK. Только после того, как пользователь нажмёт ОК, выполнять дальнейшие действия (Вариация: окно с Yes/No).

Можно привести ещё добрую сотню подобных примеров.
В случае, если бы были синхронные вызовы, никаких особых проблем бы не было. Действия выполняются одно за другим, никакой особой параллельности вычислений здесь не требуется.

Однако все вызовы в AS только асинхронные. Вызов функции возвращается немедленно, а вот действие можно быть ещё не выполнено.
Чтобы узнать, что действие завершено, необходимо подписаться на соответствующее событие.

Для каждого события необходимо создать обработчик.

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

Возникают проблемы:

1. Код разрастается и становится не таким очевидным. Например, другой разработчик, посмотрев на такой код, может не сразу понять, в каком порядке вызываются действия.

2. Вместо локальных переменных приходиться использовать приватные поля для тех данных, которые должны быть общими для разных функций.

3. Некоторые обработчики одноразовые. После срабатывания они должны быть отключены (например, обработчик hand-shake должен сработать один раз. Дальнейший сетевой обмен никак не должен его задействовать).

4. Необходимо заранее задумываться о том, какие действия у нас "мгновенные", а какие "долгоиграющие", чтобы правильно разбивать функции на части.

Попробуем всё-таки скомпоновать всё в одну функцию, чтобы частично решить проблемы 1, 2 и 3.
Вместо отдельных обработчиков будем использовать вложенные анонимные функции.

Для простоты возьмём такой пример: в начальном состоянии приложение ожидает, пока пользователь нажмёт первую кнопку.
После нажатия первой кнопки, приложение ожидает, пока пользователь нажмёт вторую кнопку.
Очень утрированный пример, но аналогия с предыдущими, думаю, вполне очевидна, зато он более нагляден в визуальном плане.

Полный пример кода находиться в аттаче, приведу только самое основное:

public function Main()
{
tf.text = "Step 0";
var message1:String = "Step 1";
var message2:String = "Step 2";

btn1.addEventListener(MouseEvent.CLICK, function():void {
tf.text = "Step 1";
btn1.removeEventListener(MouseEvent.CLICK, arguments.callee);
btn2.addEventListener(MouseEvent.CLICK, function():void {
tf.text = "Step 2";
btn2.removeEventListener(MouseEvent.CLICK, arguments.callee);
});
});
}

Мы временно устанавливаем обработчики событий, после срабатывания отключаем предыдущий обработчик и прописываем следующий (в более сложном случае - ещё и инициируем действие, например, начинаем загрузку файла или отправляем запрос серверу).

Достоинства:
1. Действия выполняются строго по порядку.
2. В целом, имеем одну функцию, в которой чётко виден порядок действий.
3. Вложенные функции имеют доступ к локальным переменном основной функции.

Недостатки:
1. Очень много "мусорного" кода, который приходиться набирать и который затрудняет чтение.

Решение: изменить компилятор таким образом, чтобы весь допольнительный код, необходимый для "прерывания" функции автоматически генерировался компилятором и не присутствовал явно в исходном коде.

Чтобы получилось что-то вроде такого:

tf.text = "Step 0";
var message1:String = "Step 1";
var message2:String = "Step 2";
bnt1.waitClick()
tf.text = "Step 1";
btn2.waitClick();
tf.text = "Step 2";

Аналогично как, когда мы пишем:
nodes = xml.items(@data=="100");
компилятор сам достраивает весь необходимый код (в частности - цикл для поиска). Мы не заморачиваемся на рутинные действия, код остаётся лаконичным и понятным.

Точно так же (теоретически) компилятор может создавать за нас всё необходимое для синхронных вызовов.

Но это чисто в теории и на первый взгляд. Интуитивно я чувствую, что здесь скрыто не мало подводных камней.
Вот что я вижу на первый взляд:

1. Есть риск, что неособо-локальные переменные (существующие даже после завершения функции, в которой они описаны) могут вызвать утечки памяти. Или наоборот - удаление нужного объекта.

2. Этот подход хорош, когда ожидается только одно событие. Но многие действия могут завершаться с разными событиями (например - если возникает ошибка: не получилось установить соединение, файл не найден, закончился таймаут и т.д.)
Поэтому, видимо, придётся сделать более сложное расширение языка.

3. Пока не проверил, как будет вести себя такая система с исключениями.

4. Потребуется разширить существующие классы, чтобы добавить синхронные методы (разумеется, никто не запрещает пользоваться асинхронными).

5. Возможно возникновение проблем, свойственных многопоточным системам.

6. Возможны проблемы с производительностью. И пока не совсем ясно, как осуществлять оптимизацию (например, как определять, когда не следует удалять обработчик, так как он ещё может пригодиться).

Вот именно эти проблемы я и хотел бы обсудить. А так же - как покрасивее сделать синтаксис языка.
Ну и наконец - что лучше брать за основу: AS3 или HaXe.

etc
10.04.2008, 10:01
Сорри, весь пост не читал, вопрос возник сразу: почему сервер шлет какие-то команды клиенту, не дожидаясь реакции пользователя? Результат выполнения чего-либо на клиенте должен быть отправлен на сервер, а тот уже решает, что делать с результатом дальше. Т.е. вот этих вот waitForClick не должно быть в клиенте, клиент логической частью не должен обладать, все решения принимает сервер. Сервер не должен быть тупым и присылать сразу очередью тучу команд логики, он должен их присылать по мере получения результатов выполнения предыдущих.

Команды, приходящие от сервера, должны отправляться в тот контроллер, который их обрабатывает. Контроллер диалогового окна может спокойно ждать реакции пользователя, а контроллер передвижения персонажа, по командам, приходящим от сервера, передвигает этого самого персонажа, совершенно не обращая внимания на то, что открыто диалоговое окно каким-то другим контроллером.
На моей практике никаких проблем в реализации подобного плана клиентов на AS3 не было, не говоря уже об отсутствии необходимости реализовывать какой-то движок мультипоточности. Ну вот не вижу я ни одной причины, по которой он должен быть.

WindWalker
10.04.2008, 14:05
waitForClick - всего лишь тривиальный пример.
А как тебе такое:

var connected:bool = false;
var attempts:int=0;
while (!connected && attempts<3) {
if (sock.connect()) {
connected = true;
} else {
attempts++;
sleep(500);
}
}

Никакого соединения с сервером ещё нет, так что свалить не на кого.

sock.connect() и sleep() в данном случае - синхронные методы.
Попробуй написать вышеприведённый код на классическом AS3, затем сравним.

-------------------------

Другой пример.
В одном проекте мне необходимо было рисовать различные псевдо-3D объекты в аксонометрической проекции. Для отрисовки каждого объекта требуется одна или несколько текстур. Текстуры хранятся во внещних файлах.
Разумеется, загружать текстуру каждый раз, когда он потребовалась - слишком дорогое удовольствие, поэтому я сделал кеш текстур, основной метод которого выглядел так:
public static function getTexture(sPath:String):BitmapData

При старте приложения загружались все необходимые текстуры (коих тогда было мало), затем объекты в любой момент могли взять нужные текстуры.

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

Но в таком случае я не могу сразу вернуть BitmapData! Ведь возможно такой текстуры в кеше ещё нет, следовательно она будет загружаться, следовательно потребуется какое-то время.
Пришлось добавлять callback'и. Но мало того - пришлось полностью переделывать отрисовку объектов. Если раньше это был один метод (у разных типов объектов - свой), то теперь мне пришлось его разбивать на части, чтобы гарантировать, что текстура точно загружена.
Если бы я мог сделать getTexture синхронным, то таких значительных переделок не потребовалось бы.

etc
10.04.2008, 14:25
Ну connect в AS3 не синхронный, начнем с этого.

Подход к текстурам неверный, поверхность должна получить адрес текстуры и все, она начнет грузить текстуру или не начнет, пусть сама решает. Если поверхность скрылась, то она может положить в общий кеш текстуру (если текстура успела загрузиться). Если поверхность скрылась, а текстура загрузилась позже, то положить сразу кеш. Кеш сам по себе очищает себя, скажем, раз в пять минут, убивая все битмапы.

Вы сваливаете свою ошибку проектирования на язык, который ничем вас на самом деле не обидел.

FlexOkeks
10.04.2008, 14:37
Из много букв, отбросив лишнее, выделил такую суть:
Возникают проблемы:

1. Код разрастается и становится не таким очевидным. Например, другой разработчик, посмотрев на такой код, может не сразу понять, в каком порядке вызываются действия.

2. Вместо локальных переменных приходиться использовать приватные поля для тех данных, которые должны быть общими для разных функций.

3. Некоторые обработчики одноразовые. После срабатывания они должны быть отключены (например, обработчик hand-shake должен сработать один раз. Дальнейший сетевой обмен никак не должен его задействовать).

4. Необходимо заранее задумываться о том, какие действия у нас "мгновенные", а какие "долгоиграющие", чтобы правильно разбивать функции на части.

Попробуем всё-таки скомпоновать всё в одну функцию, чтобы частично решить проблемы 1, 2 и 3.

Мы временно устанавливаем обработчики событий, после срабатывания отключаем предыдущий обработчик и прописываем следующий (в более сложном случае - ещё и инициируем действие, например, начинаем загрузку файла или отправляем запрос серверу). Сомнительное решение, проблему 1 например мы не только не решили, но и усугубили, она у нас оказалась главным недостатком:

Достоинства:
1. Действия выполняются строго по порядку.
2. В целом, имеем одну функцию, в которой чётко виден порядок действий.
3. Вложенные функции имеют доступ к локальным переменном основной функции.

Недостатки:
1. Очень много "мусорного" кода, который приходиться набирать и который затрудняет чтение.

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

WindWalker
10.04.2008, 14:40
Именно!
Приведённый код - пример того, как выглядел бы код, если бы connect был синхронным.

Напиши эквивалент этого кода с помощью асинхронного connect.
И мы вместе почувствуем разницу.

FlexOkeks
10.04.2008, 14:47
Попробуй написать вышеприведённый код на классическом AS3, затем сравним.
А это точно, дело привычки! :) А попробуйте (допишите сами ) на классическом (допишите сами) и посмотрим кто круче, типа того.

etc
10.04.2008, 14:50
Именно!
Приведённый код - пример того, как выглядел бы код, если бы connect был синхронным.

Напиши эквивалент этого кода с помощью асинхронного connect.
И мы вместе почувствуем разницу.
Я разницу не почуствую, мне синхронность не нужна. Набросал:


socket.addEventListener(IOErrorEvent.IO_Error, this.handler_ioError);

private var _attempts:uint = 0;

private var _timeoutID:uint;

private function connect():void {
this._attempts++;
try {
socket.connect(…);
} catch (error:Error) {
this.attempt();
}
}

private function attempt():void {
if (this._attempts > 2) {
clearTimeout(this._timeoutID);
// bla-bla, показываем ошибку

} else this._timeoutID = setTimeout(this.connect, 500);
}

private function handler_ioError(event:IOErrorEvent):void {
this.attempt();
}

Подлиннее, конечно (правильность работы не проверял, правда), но никаких потоков, sleep и прочей ботвы вообще не надо. Но это общая идеология работы AS3. AS3 != Java, хоть убей.

WindWalker
10.04.2008, 15:06
Итак, во-первых - длиннее.
Во-вторых, цикл уже не так очевиден.
В-третьих - порядок действий уже не так очевиден, появляется псевдорекурсия: connect может вызвать attempt, attempt косвенно вызывает connect.

B это всего для двух "долгоиграющих" действий: connect и таймаут.

Теперь маленькое добавление: если соединение не удалось, то показать диалоговое окно с надписью: "Connection failed" и кнопкой OK.
Делать следующую попытку только после того, как пользователь нажмёт OK.

Как меняется код на выдуманном языке:
var connected:bool = false;
var attempts:int=0;
while (!connected && attempts<3) {
if (sock.connect()) {
connected = true;
} else {
showMessage("Error", "Connection failed", [OK]);
attempts++;
sleep(500);
}
}

Ваш ход?

-------------------


Подход к текстурам неверный, поверхность должна получить адрес текстуры и все, она начнет грузить текстуру или не начнет, пусть сама решает.
А если эта текстура уже загружена?
А если другая поверхность начала загружать эту текстуру, но ещё пока не загрузила?

etc
10.04.2008, 15:15

socket.addEventListener(IOErrorEvent.IO_Error, this.handler_ioError);

private var _attempts:uint = 0;

private var _timeoutID:uint;

private function connect():void {
this._attempts++;
try {
socket.connect(…);
} catch (error:Error) {
this.attempt();
}
}

private function attempt():void {
if (this._attempts > 2) {
// bla-bla, показываем ошибку

} else {
this.showMessage("Error", "Connection failed", Message.OK);
}
}

private function handler_ioError(event:IOErrorEvent):void {
this.attempt();
}

private function handler_okClick(event:Event):void {
this.hideMessage();
this.connect();
}

Как-то так. Рекурсии не будет. В чем проблема?

WindWalker
10.04.2008, 15:30
Итого: +1 функция (новый обработчик).

addListener в одном месте, действие, которое может инициировать событие - в другом, обработчик события - в третьем, и после этого ещё и повторный вызов connect.

А теперь дальше.
QA посмотрели наше приложение и сказали: что-то слишком часто сообщение появляется. Надо сделать так, чтобы сперва делалось три попытки (молча), а если они не удались, то тогда уже показывать сообщение. Между попытками пауза 500 мс.
Сообщение должно теперь содержать YES и NO. Если отвечает YES, то снова делать три попытки.


var connected:bool = false;
var attempts:int=0;
do {
while (!connected && attempts<3) {
if (sock.connect()) {
connected = true;
} else {
attempts++;
sleep(500);
}
}

if (!connected) {
var answer = showMessage("Error", "Connection failed", [BTN_YES, BTN_NO]);
}
} while (!connected && answer == BTN_YES);

Уже чуть-чуть подлинее. Но порядок действий по-прежнему виден.

Ваш ход?

etc
10.04.2008, 15:40
Я не играю с вами в шахматы, потому как мой ход очевиден и смысла в продолжении нет. Если вы ради сомнительного удобства собираетесь ваять конвееры и псевдомногопоточность — вперед. Но это AS way, и это не Java, в очередной раз говорю.
Вам всего навсего нужен sleep, а многопоточность тут особо и не нужна.
Чего вы хотите примерами кода доказать — я не знаю. Я вам докажу, что можно без многопоточности, пусть и длиннее, но никаких проблем в понимании логики лично я не вижу. Если вы видите, то AS — не ваше.

WindWalker
10.04.2008, 17:00
Я нигде не утверждал, что вообще нельзя обойтись средствами AS.
Как так раз таки наоборот - можно.

Просто получается:
1. Гораздо длиннее
2. Сложнее вносить изменения
3. Порядок действий теряется, так как размазан по всему коду.
4. Нужно следовать строго определённой структуре кода. Некое единое по своей сути действие необходимо разбивать на несколько кусочков.

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

Вот вы уже быстро сдались даже на таком простом примере. На практике это означало бы "won't fix, потому что всю систему ломать надо".

Я ведь не зря привёл пример с E4X. Жаль, что вы не прочитали тот пост целиком.
Ведь работать с XML можно и без E4X. Почему же все так фанатеют от него?
Потому что компилятор берёт на себя заботу о всякой рутине.
Мы пишем:
nodes = xml.item.(@data="100");
А компилятор создаёт за нас цикл.
Мы можем легко изменять единственную строчку, а реализацию целиком возьмёт на себя компилятор.

В AS3 массивы нетипизированные. Разумеется, эту проблему можно обойти: каждый раз при обращении к элементу массива явно делать преобразование типа. Но это, во-первых, добавляет множество лишнего по сути кода, во-вторых, увеличивает шанс допустить ошибку (которую компилятор обнаружить не сможет).
Тем не менее в haXe массивы вполне даже типизированные. И не только массивы. И даже используются допольнительные трюки, чтобы оставить строгую типизацию даже в run-time (чтобы не тратить время на приведение типов).

В AS3 нет типа enumaration, и без него вполне можно обойтись. Но в haXe он есть и позволяет многие вещи сделать короче, элегантнее и более защищённым от ошибок.

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

Чисто теоретически это сделать можно - ведь, как все мы прекрасно знаем (и в данной теме ещё раз продемонстрировали), сделать на AS3 код, аналогичный коду с синхронными вызовами, возможно.
Но поддерживать его гораздо сложнее.

etc
10.04.2008, 17:28
Я не вижу в споре (?) и в сравнении двух разных языков никакого смысла и в такие шахматы с разными фигурами и правилами с каждой стороны не играю.

В многопоточности тоже есть свои недостатки, потоки нужно контроллировать, вовремя останавливать, жрут больше памяти и т.п. Все это приводит к неменьшему количеству кода, только в других местах.
И проблемы с поддержкой реализации «многопоточности» на AS3 сводят на нет преимущество в длине кода и мнимой простоте.
Есть два инструмента, надо использовать каждый в своей сфере, а не пытаться сделать из одного инструмента кривую обрезанную реализацию другого ради кривой и чуждой изначальному инструменту логике. Из рубанка плохой молоток выйдет, равно как и наоборот.

WindWalker
10.04.2008, 20:35
Если вы ради сомнительного удобства

С таким консервативным подходом "сомнительное удобство", "это не Java" можно зайти очень далеко.

- Зачем нужны синхронные методы, если тоже самое можно сделать с помощью асинхронных, это же не Java?
- Зачем нужен Flex, если всё то же самое можно сделать на Flash?
- Зачем нужен E4X, если есть XMLDocument и можно тоже самое сделать с помощью firstChild, nextSibling?
- Зачем в свою очередь нужен XMLDocument, если можно взять обычный String и самому распарсить?
- Зачем нужны пекеджи и классы, у нас ведь не Java?
- Зачем нужен JIT-компилятор, у нас ведь не Java?
- Зачем нужны типы int и uint, если есть Number?
- Зачем вообще нужно использовать разные типы, если есть *?
- Зачем был нужен AS2, если тоже самое можно сделать на AS1?
- Зачем нужны внешние .as файлы, если можно писать код в кадрах?
- Зачем нужны классы, если объекты можно создавать и без них?

А всё просто: как вы сами сказали, всему своё место. Иметь два инструмента лучше чем один - всегда можно выбрать, какой из них больше подходит для решения конкретной задачи.
Вы же говорите: "У меня уже есть рубанок, зачем мне какие-то молотки?"

Использовать события хорошо в тех случаях, когда порядок действий (и соответственно порядок возникновения событий) заранее неопределён и непредсказуем.
Но если нужно выполнить действия строго по порядку, то события и колбеки выглядят как подпорки.
Но мы может сделать более высокоуровневую конструкцию, которая спрячет от нас все эти подпорки и костыли и позволит нам абстрагироваться от них.
Как E4X по сравнению с XMLDocument, как class по сравнению с prototype.

Смыслом нашей "игры без правил" было:
1. Показать, что для кода с синхронными методами можно создать аналогичный код с помощью событий и колбеков (а значит, в теории, можно автоматизировать этот процес).
2. Показать преимущества синхронных методов в при решении определённого класса задач (это совсем не означает, что синхронные методы рулят всегда и везде).

Правда, я проиграл. Я надеялся, что у меня всё-таки получится убедить, что синхронные методы действительно были бы полезны.
Но вместо этого получил ответ в стиле: "Да зачем нам ваши классы, когда всё прекрасно делается через прототипы. Если у вас проблемы с пониманием логики, значит AS не для вас. Классы пусть останутся в Java, а здесь им не место".

--------------------
Опять вместо конструктивной беседы получился спор "да зачем это надо".
Где бы найти собеседников, а не оппонентов...

wolf3000
10.04.2008, 22:01
Господа, давайте делать разницу между потоками и процессами.
Ява работает в одном потоке но может создать тучу процессов.

Флеш же явно трудится в одном потоке. При этом псевдо многопоточность реализована просто убого - без приоритетов и пр.

И закралось у меня сомнение, что запуск нескольких псевдо потоков просто убипает плеер... Соответственно флексовые ивенты и таймеры масдай!

FlexOkeks
11.04.2008, 00:06
А всё просто: как вы сами сказали, всему своё место. Иметь два инструмента лучше чем один - всегда можно выбрать, какой из них больше подходит для решения конкретной задачи. Если так рассуждать, то ответте на простой вопрос, зачем стоматологу отбойный молоток? По вашему рассуждению получается что стоматологу лучше его иметь, для решения конкретной задачи. :quiet:

WindWalker
11.04.2008, 00:30
Этот вопрос вам лучше адресовать стоматологам где-нибудь на стоматолог.ру или ещё где-нибудь. Хотя сомневаюсь, что перед ними часто встают задачи, требующие отбойного молотка.

А здесь форум для флешеров. И я привёл примеры задач, которые часто встают перед флешерами. В том числе и у меня, но не только.
Обратите внимание, что тему создал не я. Есть другая, ныне закрытая, тема, тоже созданная не мной. Есть масса попыток создания библиотек, для решения подобных задач (далеко не единственный пример: http://potapenko.com/rus/articles/conveyor.htm). Это говорит о том, что задача крайне распространённая.

И синхронный вызов методов - самый простой, самый интуитивно понятный метод её решения. Это не отбойный молоток, это как раз таки современная немецкая ультрозвуковая безконтактная бормашина.
Но у нас такой, увы, нет, поэтому приходится по старинке пользоваться обычной бормашиной со сверлом (ивенты, таймеры) и вздыхать, что чудеса техники есть только в Германии (в Java), а мы живём в России (в Flash) и не надо путать.

etc
11.04.2008, 00:42
WindWalker, ну пишите тогда на Java, а то вы как ВовкаМорковка, ходите и ноете.
Вещи, которые вам хочется, должны быть реализованы на уровне языка, а не в виде поделки. Иначе это бесполезный треп. Работать надо, а не вздыхать на тему «Вот как было бы круто, если бы была такая вещь…». Нет ничего идеального, машина не может делать вообще все за вас.

WindWalker
11.04.2008, 00:59
ну пишите тогда на Java
Ну а вы тогда пишите на AS1.0. В кадрах.
Зачем вам все эти классы, типы, массивы, E4X, метаданные и прочее?
gotoAndPlay, gotoAndStop - вот он настоящий флеш, всё остальное от лукавого. Так?

Вещи, которые вам хочется, должны быть реализованы на уровне языка
Именно! А чём я и твержу уже давно, но все делают вид, что не понимают.
Ещё раз намекаю: AS2 и AS1 - разные языки, но работают на одной и той же виртуальной машине. Код из AS2 прекрасно преобразуется в AS1.
Означает ли это, что AS2 - ненужная поделка? С вашей точки зрения - получается, что так.

etc
11.04.2008, 01:07
Так?
Не так. Это — эволюция. Есть инструмент — надо использовать. Нету — нефиг придумывать кривые реализации, я не пытаюсь взлететь, если у меня нет крыльев.
Причем тут AS1 и кадры? Не было бы всего того, что было перечислено вам выше, я бы и писал в кадрах. Каким боком здесь многопоточность, которой нет в языке и которую вы пытаетесь криво реализовать?
Я не могу применять то, чего нет, но я не утверждаю того, что не буду применять то, что уже есть.

Именно! А чём я и твержу уже давно, но все делают вид, что не понимают.
Ещё раз намекаю: AS2 и AS1 - разные языки, но работают на одной и той же виртуальной машине. Код из AS2 прекрасно преобразуется в AS1.
Означает ли это, что AS2 - ненужная поделка? С вашей точки зрения - получается, что так.
AS2 это вообще что-то с чем-то и требует отдельного обсуждения, но я в нем не хочу участвовать.

WindWalker
11.04.2008, 01:42
Каким боком здесь многопоточность, которой нет в языке и которую вы пытаетесь криво реализовать?
1. А откуда взялись классы в AS2? Да, реализованы они были не лучшим образом. Но тем не менее вы ими пользовались? И считали это эволюционным шагом?
2. Мы уже убедились, что код с синхронными методами прекрасно переводиться в код на AS3 (в чём я в принципе и не сомневался). Вы лично выступали в роли транслятора. Осталось это лишь автоматизировать, поскольку обычно в роли транслятора приходиться выступать мне самому, а хочется свалить это на компилятор.

И вот как раз таки проблемы автоматизации я и хотел бы обсудить. Но, видимо, выбрал не то место для серьёзных бесед. :(

etc
11.04.2008, 09:42
1. А откуда взялись классы в AS2? Да, реализованы они были не лучшим образом. Но тем не менее вы ими пользовались? И считали это эволюционным шагом?
Да, считал. Но это было недоклассы и проблем с ними было много. Но при этом, для писаны на AS2 были созданы прекрасные и удобные инструменты.

2. Мы уже убедились, что код с синхронными методами прекрасно переводиться в код на AS3 (в чём я в принципе и не сомневался). Вы лично выступали в роли транслятора. Осталось это лишь автоматизировать, поскольку обычно в роли транслятора приходиться выступать мне самому, а хочется свалить это на компилятор.
Компилятор не должен ничего транслировать, такие вещи должны быть реализованы нативно. Трансляция — полумера, если будут косяки, то контролировать их не получиться. E4X должен был быть реализован на более низком уровне, а не тучей циклов на AS3. RegExp тоже.
Многопоточность надо реализовывать как раз на низком уровне, иначе это будет псевдомногопоточность.

Но, видимо, выбрал не то место для серьёзных бесед. :(
Вы знаете, можно долго полемизировать по этому поводу, но лично вы компилятор не измените. Работайте с тем, что есть, здесь все-таки сидят практики, а не теоретики. Язык эволюционирует и это хорошо.
И надо не забывать, что флешовый runtime весит около двух мегабайт, а не пятнадцати, как Java. Для веба размер runtime очень критичен, поэтому не все сразу.

И что именно вы хотите обсудить? Как было было прекрасно, если… и повздыхать по этому поводу? Пути реализации runtime-транслятора на AS3? Если да, то лично я не вижу смысла в такой штуке, реализовывать на ней что-либо и потом бороться с глюками, теряя время и деньги неоправданно.

FlexOkeks
11.04.2008, 12:18
И синхронный вызов методов - самый простой, самый интуитивно понятный метод её решения. Это не отбойный молоток, это как раз таки современная немецкая ультрозвуковая безконтактная бормашина.
Но у нас такой, увы, нет, поэтому приходится по старинке пользоваться обычной бормашиной со сверлом (ивенты, таймеры) и вздыхать, что чудеса техники есть только в Германии (в Java), а мы живём в России (в Flash) и не надо путать.Есть одна маленькая деталь, самая современная безконтактная бормашина не будет работать без электричества! Вы же предлагаете строить целую электростанцию для небольшой партии ультрасовременных машин. Решить эту проблему проще съездить в германию (сделать на java) на уровне конкретно взятого примера, а на уровне всея Руси ( флеш плеера) это занятся строительством электростанций, компиляторов, теоретически это полезно, но практически полезно иметь для начала хотябы Flash Player SDK, к чему все идет. А тема поднята слишком возвышенная для данной ветки, если про стоматологов надо задавать на стоматолог ру, то и раскрывать тему многопоточности во флэш стоило бы в подходящем разделе и с самого начала по порядку. :victory:

WindWalker
11.04.2008, 13:29
Давайте переживать трудности по мере их поступления (с) Жванецкий

Никто не застрахован от багов и глюков. В том же флеш-плеере, флексе и адобовском компиляторе их предостаточно.

Сейчас у меня, так сказать, стадия проектирования. Я как раз хочу заранее предусмотреть, с какими проблемами придётся столкнуться и либо найти путь их решения, либо всё-таки осознать, что да, действительно, вся затея обречена на провал. Но пока ни одной такой роковой проблемы озвучено не было.

Да, я не профессионал в области создания компиляторов, однако некоторый опыт всё же имеется. К тому же, в данном случае не нужно писать всё с нуля - только изменить существующие. Исходники haXe доступны давно, исходники флексового компилятора тоже недавно были открыты - есть из чего выбрать.
На худой конец сделать именно транслятор, на выходе у которго не байт-код, а код на AS3.

Реализовать псевдомногопоточность (мне больше нравится термин интегрированная многоконвеерность) можно разными способами. Я ожидал как раз таки рассмотрения этих способов и выбора наиболее оптимального из них (по критериям безопасности, затрат ресурсов и т.д.).

etc
11.04.2008, 14:58
К сожалению, на тему конвееров я с вами не собеседник, у меня острой необходимости в их использовании не было.

Kolan
09.01.2009, 15:30
__etc, у меня к вам вопрос. В посте № 28 вы привели код


socket.addEventListener(IOErrorEvent.IO_Error, this.handler_ioError);

private var _attempts:uint = 0;

private var _timeoutID:uint;

private function connect():void {
this._attempts++;
try {
socket.connect(…);
} catch (error:Error) {
this.attempt();
}
}

private function attempt():void {
if (this._attempts > 2) {
clearTimeout(this._timeoutID);
// bla-bla, показываем ошибку

} else this._timeoutID = setTimeout(this.connect, 500);
}

private function handler_ioError(event:IOErrorEvent):void {
this.attempt();
}

Который считаете аналогом кода:

var connected:bool = false;
var attempts:int=0;
while (!connected && attempts<3) {
if (sock.connect()) {
connected = true;
} else {
attempts++;
sleep(500);
}
}


Какой же это аналог? Тут же нет ожидания.

Пройдем по шагам.
Клиент (под клиентом подразумевается код, который вызывает ваш) вызывает connect()

Допусти ответа нет, тогда вызывается attempt() в котором выполняется this._timeoutID = setTimeout(this.connect, 500); и всё, на этом метод connect() заканчивается? В этом случае код клиента пойдет дальше, неизвестно куда.

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

Объясните, где же тут аналог?

BlooDHounD
09.01.2009, 16:12
Kolan, ждать будет один поток, а не весь клиент.

Kolan
09.01.2009, 18:56
BlooDHounD, помогите разобраться. Что значит один поток? Что это за поток и откуда он берется?

И главное, что заставит его ждать? setTimeout? Ведь это просто запуск таймера. Таймер просто запуститься и ждать его никто не будет.

Рассмотри пример того же WindWalker, который который он приводит в обсуждении на gotoandflash.ru (http://gotoandflash.ru/forum/index.php?showtopic=3003):

Он говорит, что аналогом кода
function test():void {
trace ("Before");
delay(1000);
trace ("After");
}

В AS будет:
function test():void {
trace ("Before");
setTimeout(test_part2, 1000);
}

function test_part2():void {
trace ("After");
}

Но ведь это же неправда. Это же никакой не налог. Я реализовал второй вариант (привожу весь модуль):
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
<mx:Script>
<![CDATA[

private function init(): void {
textArea.text = textArea.text + "1";
doTest()
textArea.text = textArea.text + "2";
}

private function doTest(): void {
textArea.text = textArea.text + "Before";
setTimeout(test_part2, 1000);
}

private function test_part2():void {
textArea.text = textArea.text + "After";
}

]]>
</mx:Script>
<mx:TextArea x="32" y="37" width="333" height="233"
id="textArea"/>

</mx:Application>

В результате в текстовое поле сначала записывается 1Before2, а потом, через секунду добавляется After. То есть строка получается такой: 1Before2After. Если бы блокировка была, то она, что логично, была бы 1BeforeAfter2.

Это означает, что никакого ожидания нет и такой вариант (с setTimeout(test_part2, 1000)) — это совершенно не аналог delay(1000);.

Совсем непонятно, почему и WindWalker и __etc говорят о их идентичности.

DarkLight
09.01.2009, 19:17
Было сказано про аналогичность в рамках конкретной локальной задачи. Если говорить о реальной реализации - флексовый компилятор опенсорсен, дорабатывайте под свои нужды(например, такой доработкой были реализованы вещи типа code coverage для flex) для симуляции delay.

Kolan
09.01.2009, 19:51
Я не хочу дорабатывать или флеймить, я хочу понять как устроен Флекс.

Даже в рамках конкретной задачи код __etc, о котором я говорил ранее в посте № 45 не решает задачу, которую просит решить WindWalker.

etc
09.01.2009, 23:16
Всё просто: в ActionScript нет ни sleep, ни delay, т. е. остановить поток нельзя. Но вместо остановки можно просто ничего не делать и повесить вызов нужной функции по таймеру.

Kolan
10.01.2009, 00:11
__etc, я правда вас не понимаю. А что делать, если код устроен так, что нужно дождаться окончания чего-то? Вы понимаете, что ваши варианты с таймером не подходят, потому, что они все равно асинхронные?

Правильно ли я понимаю, что единственный прием, который сейчас используют разработчики на Флексе, чтобы создать синхронность — это посылка следующего запрос внутри обработчика получения предыдущего?

Считаю, что это плохая практика, которая влечет за собой или очень сложную логику, которую также сложно модифицировать (из-за «запаха» Shotgun Surgery) или на что-то просто плюют и не делают все проверки.

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

Прошу, поругайте мою идею. А может есть еще какие-то приемы?

etc
10.01.2009, 00:17
Специфика работы в ActionScript как раз и состоит в том, что загрузка любых данных, коннекты и прочее — асинхронно (нет никакой синхронности в ActionScript, все примеры кода, приведенные здесь, являются асинхронными). А для работы с асинхронными вызовами существует событийная модель, пишутся хендлеры, подписываются на соответствующие события и все довольны. Не говоря уже об отсутствии каких-либо неудобств (на мой взгляд). Да, код получается длиннее, зато мы не заботимся о контроле над потоком, потому что он всегда выполняется.

Собственно, вам требуется изучить основные моменты при работе с ActionScript 3.0. Мой пример кода просто делает таймаут на соединение и три попытки и, собственно, всё. Т. е. я ничего не дожидаюсь.

Kolan
10.01.2009, 01:01
__etc, событийная модель мне знакома. Она есть во многих языках и хорошо уживается с синхронными вызовами. Ваш код я понял.

Неудобство асинхронности в том, что все разбросано по методам. Если, например, по логике после connect надо будет загрузить объект А, то в WindWalker со совоим кодом нипишет:

connect();
load(A);


А вам придется делать load(A); в обработчики успешного подключения, хотя подключение и загрузка вещи не особенно связанные. То есть ваш код будет обладать низким зацеплением, если тут можно использовать этот термин. Ваш код сложнее модифицировать, его нельзя окинуть взглядом, так как он находится во множестве мест... Всё это плохие запахи.

В итоге ветки, думаю, можно сделать такой вывод:
ActionScript по сути асинхронен. Вместо того, чтобы пытаться сделать синхронные вызовы надо просто рассчитывать на это. Ваш код должен «понимать», что событие может произойти когда угодно и работать корректно в этих условиях.

etc
10.01.2009, 01:51
Я бы сказал, что AS в сути вообще синхронности не содержит.

Jean
06.02.2009, 16:04
все-таки неудобно без синхронности. приведу пример:
для валидации форм используется класс Validator, а именно метод doValidation(...):Array, который возвращает результаты проверки. а что если я хочу проверить форму на сервере? как мне быть в таком случае?

как раз тут неплохо было бы сделать sleep для потока исполнения программы и подождать что вернет сервер. Так сделать нельзя, поэтому приходиться отказываться от удобного инструмента Validator :(

Kolan
16.09.2009, 12:41
Хотя прошел почти год — сегодня я написал небольшой пост про то, как мы в итоге поступили с синхронностью в КСИНО (http://www.ksoftware.ru/wiki/ksino) (в системе, разрабатывая которую я и задавал тут вопросы).

Суть в том, что мы сделали все асинхронным до мозга костей. В частности использовали паттерн Активная запись.

Ссылка на пост.
http://ksoftware.ya.ru/replies.xml?item_no=1970