Ответ 1
возможно, было бы более разумно, чтобы работал только один таймер. насколько я знаю, работающему таймеру нужна целая нить... чтобы поместить его в псевдокод, основной код потока таймера - это что-то вроде этого...
while (input.isEmpty()) {
wait(interval);
output.add({timerId:thisId, tickId: tickId++});
}
вывод, являющийся dequeue, основной поток (который выполняет ABC) проверяет каждый раз, а затем... имея много таймеров, у вас будет много потоков, что лишние накладные расходы... также для каждого события сообщение отправленный с таймера на основной поток, должен быть выбит из детекса, что дорого, поскольку он должен быть потокобезопасным... и затем необходимо найти соответствующий таймер, необходимо создать событие таймера (выделение также довольно дорого), а затем отправляется, что также связано с несколькими вызовами...
поэтому попробуйте использовать один таймер или используйте setInterval... также, подумайте, что модель Event во флэш-памяти довольно приятная, но дорогая... она используется для развязки, чтобы обеспечить приятную архитектуру... для по той же причине, это не хорошо для критических ситуаций производительности... еще раз, отправка события дорого...
Я сделал небольшой класс, это немного более ручной (просто для того, чтобы понять, хотя он теоретически используется):
package {
import flash.utils.*;
public class Ticker {
//{ region private vars
private var _interval:int;
private var _tick:uint = 0;
private var _tickLength:Number;
private var _callBacks:Dictionary;
//} endregion
public function Ticker(tickLength:Number = 0) {
this.tickLength = tickLength;
this._callBacks = new Dictionary();
}
//{ region accessors
/**
* the current tick
*/
public function get tick():uint { return _tick; }
/**
* the tick length. set to a non-positive value, to stop ticking
*/
public function get tickLength():Number { return _tickLength; }
public function set tickLength(value:Number):void {
if (this._tickLength > 0) clearInterval(this._interval);
if ((this._tickLength = value) > 0) this._interval = setInterval(this.doTick, value);
}
//} endregion
/**
* add a callback, to be called with every tick
* @param callback function (tick:int):*
*/
public function addCallback(callback:Function):void {
this._callBacks[callback] = callback;
}
/**
* removes a callback previously added and returns true on success, false otherwise
* @param callback
* @return
*/
public function removeCallback(callback:Function):Boolean {
return delete this._callBacks[callback];
}
/**
* executes a tick. actually this happens automatically, but if you want to, you can set tickLength to a non-positive value and then execute ticks manually, if needed
*/
public function doTick():void {
var tick:uint = this._tick++;//actually, this is only superspicion ... amazingly, this makes no difference really ... :D
for each (var callback:* in this._callBacks) callback(tick);
}
}
}
он неплохо работает... здесь класс сравнения (вы можете просто использовать его как класс документа во флопе, если используете CS3/CS4):
package {
//{ region imports
import flash.display.*;
import flash.events.*;
import flash.sampler.getSize;
import flash.system.System;
import flash.text.*;
import flash.utils.*;
//} endregion
public class Main extends MovieClip {
//{ region configuration
private const timers:Boolean = false;//true for Timer, false for Ticker
private const delay:Number = 500;
private const baseCount:uint = 10000;//base count of functions to be called
private const factor:Number = 20;//factor for Ticker, which is a little more performant
//} endregion
//{ region vars/consts
private const count:uint = baseCount * (timers ? 1 : factor);
private const nullMem:uint = System.totalMemory;//this is the footprint of the VM ... we'll subtract it ... ok, the textfield is not taken into account, but that should be alright ... i guess ...
private var monitor:TextField;
private var frameCount:uint = 0;
private var secCount:uint = 0;
//} endregion
public function Main():void {
var t:Ticker = new Ticker(delay);
var genHandler:Function = function ():Function {
return function (e:TimerEvent):void { };
}
var genCallback:Function = function ():Function {
return function (tick:uint):void { };
}
for (var i:uint = 0; i < count; i++) {
if (timers) {
var timer:Timer = new Timer(delay, 0);
timer.addEventListener(TimerEvent.TIMER, genHandler());
timer.start();
}
else {
t.addCallback(genCallback());
}
}
this.addChild(this.monitor = new TextField());
this.monitor.autoSize = TextFieldAutoSize.LEFT;
this.monitor.defaultTextFormat = new TextFormat("_typewriter");
this.addEventListener(Event.ENTER_FRAME, function (e:Event):void { frameCount++ });
setInterval(function ():void {
monitor.text = "Memory usage: "
+ groupDidgits(System.totalMemory - nullMem)
+ " B\navg. FPS: " + (frameCount /++secCount).toPrecision(3)
+ "\nuptime: " + secCount + "\nwith " + count + " functions";
}, 1000);
}
private function groupDidgits(n:int,sep:String = " "):String {
return n.toString().split("").reverse().map(function (c:String, i:int, ...rest):String { return c + ((i % 3 == 0 && i > 0) ? sep : ""); } ).reverse().join("");
}
}
}
на моей машине с 60 целевым набором FPS я получаю среднее значение FPS 6.4 (через 3 минуты) и использование памяти 10-14 МБ (флуктуация возникает из-за того, что объекты TimerEvent должны быть собраны в мусор) для 10000 функций, вызывается с таймерами... с использованием другого класса, я получаю 55,2 FPS с использованием памяти 95,0 МБ (очень постоянный, флуктуации - не более 1%), причем 200000 функций вызываются напрямую... это означает, что при коэффициенте 20 вы получаете частоту кадров, которая в 9 раз выше, и вы используете только 8 раз больше памяти... это должно дать вам представление о том, сколько отпечатков создается таймером...
это должно дать вам приблизительную идею, в каком направлении идти...
[edit] Меня спросили, почему я использую частные vars... вопрос философии... мое правило: никогда не позволяйте никому извне изменять состояние вашего объекта напрямую... Представьте, что Ticker::_tickLength
был protected
... кто-то подклассифицирует его и записывает в эту переменную... с каким эффектом? значение Ticker::tickLength
будет отличаться от длины интервала... я действительно не вижу преимущества...
также, частные поля действительны только в классе... что означает, что любой может переопределить их в подклассах без каких-либо столкновений...
если я думаю, что подклассы должны иметь способ protected
, чтобы вступить в силу в состоянии, определенном в суперклассе, я создаю установщик protected
... но все же я могу реагировать... я могу изменить/проверять/зажимать значение, вводить аргумент и ошибки диапазона по желанию, отправлять события и т.д.... если вы пишете класс, вы сами несете ответственность за поддержание целостности его состояния и влияния на его поведение...
не подвергайте внутреннюю работу вашего класса... вам может потребоваться изменить их, нарушить зависимый код... а также: подклассификация сильно переохлаждена...:)
поэтому почему... [/edit]
Greetz
back2dos