Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   Flex (http://www.flasher.ru/forum/forumdisplay.php?f=84)
-   -   Многопоточность и асинхронный вызов методов во Flex (http://www.flasher.ru/forum/showthread.php?t=110724)

LexeY4eg 07.04.2008 22:23

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

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

Столкнулся с тем, что во 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

Цитата:

Сообщение от __etc
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

Цитата:

Сообщение от WindWalker
Не надо путать. Flash Player работает в одном потоке.
Это не мешает коду на ActionScript быть многопоточным.

Это что-то типа псевдомногопоточности, типа как по докумментам задания расчитаны на 10 сотрудников и зарплата на 10 человек выделяется а работает на самом деле один за десятерых, которому иногда помогают знакомые, ну там на сервер с запросом сбегать или анимацию прокрутить :quiet:

WindWalker 08.04.2008 17:07

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

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

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

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

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

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

Вобщем, скажем так:
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

Цитата:

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

Исходников корных классов флешплеера (пакет flash.*) на AS3 не существует. SDK их ниоткуда не компилирует. Флешплеер предоставляет уже готовые интерфейсы, реализации которых написаны на других языках разработчиками самого плеера.
Цитата:

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

Считайте что в реализации некоторых корных классов (Timer, Loader, Stream) используются инкапсулированные потоки исполнения.

WindWalker 08.04.2008 23:56

Цитата:

Сообщение от __etc
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
Итак, постановка задачи...

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

Примеры:

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

Из много букв, отбросив лишнее, выделил такую суть:
Цитата:

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

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

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

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

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

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

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

Сомнительное решение, проблему 1 например мы не только не решили, но и усугубили, она у нас оказалась главным недостатком:
Цитата:

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

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

Решение: изменить компилятор таким образом,

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

WindWalker 10.04.2008 14:40

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

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

FlexOkeks 10.04.2008 14:47

Цитата:

Сообщение от WindWalker
Попробуй написать вышеприведённый код на классическом AS3, затем сравним.

А это точно, дело привычки! :) А попробуйте (допишите сами ) на классическом (допишите сами) и посмотрим кто круче, типа того.

etc 10.04.2008 14:50

Цитата:

Сообщение от WindWalker
Именно!
Приведённый код - пример того, как выглядел бы код, если бы 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
Подход к текстурам неверный, поверхность должна получить адрес текстуры и все, она начнет грузить текстуру или не начнет, пусть сама решает.

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

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

Цитата:

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

Если так рассуждать, то ответте на простой вопрос, зачем стоматологу отбойный молоток? По вашему рассуждению получается что стоматологу лучше его иметь, для решения конкретной задачи. :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

Цитата:

Сообщение от WindWalker
Так?

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

Цитата:

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

AS2 это вообще что-то с чем-то и требует отдельного обсуждения, но я в нем не хочу участвовать.

WindWalker 11.04.2008 01:42

Цитата:

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

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


Часовой пояс GMT +4, время: 16:24.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.