може би би било по-логично да има само един работещ таймер по този въпрос ... доколкото знам, работещият таймер се нуждае от цяла нишка ... за да го постави в псевдокод, основният код на нишката на Timer е нещо подобно ...
while (input.isEmpty()) {
wait(interval);
output.add({timerId:thisId, tickId: tickId++});
}
изходът е деопашка, главната нишка (която изпълнява ABC) проверява от време на време ... като имате много таймери, ще имате много нишки, което е ненужно натоварване ... също така, за всяко събитие, съобщението, изпратено от таймерът към основната нишка трябва да бъде изваден от deque, което е скъпо, тъй като трябва да е безопасен за нишки ... и след това трябва да се намери съответният таймер, трябва да се създаде събитие на таймера (разпределението също е доста скъпо ) и след това изпратени, което също е въпрос на множество обаждания ...
така че опитайте да имате ЕДИН таймер или използвайте 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);
}
}
}
той се представя доста добре ... тук е клас за сравнителен анализ (трябва да можете просто да го използвате като клас на документ във fla, ако използвате 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 MB използване на паметта (флуктуацията идва от факта, че обектите на TimerEvent трябва да бъдат събрани за боклук) за 10 000 функции, извиквани с таймери ... използвайки другия клас, получавам 55,2 FPS с 95,0 MB използване на паметта (много постоянно, колебанията са под 1%) с 200 000 функции, които се извикват директно ... това означава, че при фактор 20 получавате кадрова честота, която е 9 пъти по-високо и използвате само 8 пъти повече памет ... това трябва да ви даде представа колко отпечатък създава таймер ...
това трябва да ви даде груба представа в каква посока да вървите ...
[редактиране] ме попитаха защо използвам private vars ... въпрос на философия ... моето правило: никога не позволявайте на някой отвън да променя директно състоянието на вашия обект ... представете си Ticker::_tickLength
беше protected
... някой го подкласира и записва в тази променлива ... с какъв ефект? стойността на Ticker::tickLength
ще бъде различна от дължината на интервала ... наистина не виждам предимство ...
също така частните полета са валидни само в клас ... което означава, че всеки може да ги предефинира в рамките на подкласове без никакви сблъсъци ...
ако смятам, че подкласовете трябва да имат protected
начин да влязат в сила върху състоянието, дефинирано в суперкласа, правя protected
сетер ... но все пак мога да реагирам ... мога да променя/потвърдя/захвана стойността, хвърлям грешки в аргументи и диапазони по желание, събития за изпращане и така нататък ... ако напишете клас, вие сами сте отговорни за поддържането на целостта на неговото състояние и ефектите върху поведението му ...
не разкривайте вътрешната работа на вашия клас ... може да се наложи да ги промените, разбивайки зависим код ... и също: подкласовете са изключително надценени ... :)
затова... [/edit]
поздравявам
back2dos
person
back2dos
schedule
09.07.2009