Non blocking loop – homework

추가합니다. 소스를 대거(?) 수정해서 다시 올립니다. 클래스도 하나 더 늘어나고 코드양도 길어져서 그냥 프로젝트파일 전부를 압축해서 올립니다.

클래스명도 제 마음대로 Process 에 Thread 라고 막 갔다 붙혔습니다. ㅋㅋㅋ 뭐 대충 비스무리한 기능을 하니까요. 그렇다고 정말 Thread 로 오해 하시면 안됩니다. ㅎㅎㅎ그리고 클래스를 늘리면서 몇몇 기능들을 추가해봤습니다. 보통 이런것들에 있는 sleep 과 kill 입니다. 단어는 거창하지만 그냥 ENTER_FRAME 에서 함수를 실행 여부만 결정합니다. Thread 클래스에 각 runner 와 total, limit 들을 넣을까 말까 고민하다 그냥 안넣었습니다. runner 와 limit 는 그닥 알 필요가 없을 것 같고 total 은 ThreadEvent 에서 전달해 주기 때문 입니다.

소스받아서 확인해보기 귀잖은 분을 위해 데모 갑니다.

thread_a 에는 start, complete 이벤트만 걸려 있습니다. complete 되면 총 작동시간을 볼 수 있습니다. thread_b 에는 progress, interrupt 이벤트가 걸려 있습니다. thread_b 는 count가 3이면 2초간 sleep 했다가 다시 count 가 8 일때 kill 됩니다. 작동시간이 다른건 두 쓰레드의 limit 가 다르기 때문입니다. TEST 버튼을 막 눌러보시면 쓰레드들이 잘도 작동 하는 것을 확인 할 수 있습니다. 더욱 관심있으신 분은 소스를 받아서 보셔도 좋을 것 같습니다.

오랜만에 완전 재밌는 연구를 해봤습니다. 솔직히 IEventDispatcher 구현도 첨 해봤다는;;;;ㅋㅋㅋ 으흐흐~ 이제 이걸 어디다 써먹을지 고민해 봐야겠네요. 아래 이전 소스는 기억을 위해 그냥 남겨 둡니다.

Download : nonbrocking

이벤트와 콜백에 관한 재미난 글이 있어 추가합니다. 본문도 좋지만 덧글에 남겨진 의견들도 매우 흥미롭네요.
http://www.ddongkang.com/124

—————– ♕ ———————

hika님이 작성하신 Non blocking loop 의 과제를 해봤습니다. 역시 눈으로 보고 이해하는 것과 손으로 직접 써보는 건 하늘과 땅 차이! 그냥 볼때는 그리 어렵지 않게 느껴졌는데 막상 해보니 쉽지 않네요. 특히 가장 핵심이되는 loop 함수의 경우 변수scope 를 제대로 이해하지 않고 있으면 매우 난해한 부분이 될 수도 있습니다. 저 역시 그냥 대충 이해하고 있는 터라 참 헷갈리네요. ㅎㅎ

과제니까 일단 코드 부터 보겠습니다.
LoopShape.as

package
{
	import flash.display.Shape;
	import flash.events.Event;

	public class LoopShape extends Shape
	{
		private var _this:LoopShape;
		public function LoopShape()
		{
			_this = this;
		}

		public function loop( $counter:int, $limit:int, $runner:Function, ...$param ):void{
			var counter:int;
			_this.addEventListener( Event.ENTER_FRAME, function( $e:Event ):void{
				var i:int;
				while( i++ < $limit ){
					if( counter < $counter ){
						if( $param.length ){
							$runner.apply( null, $param );
						}else{
							$runner();
						}
						if (counter == 0) {
							dispatchEvent(new LoopEvent(LoopEvent.START, $runner, counter, $counter));
						}else{
							dispatchEvent(new LoopEvent(LoopEvent.PROGRESS, $runner, counter, $counter));
						}
						++counter;
					}else {
					   _this.removeEventListener( $e.type, arguments.callee );
					   dispatchEvent(new LoopEvent(LoopEvent.COMPLETE, $runner, counter, $counter));
					   i = $limit; // 일단 이 while 에 들어오면 $limit 까지 반복하므로 제한합니다.
					}
				}
			});
		}
	}
}</pre>
<strong>LoopEvent.as</strong>
<pre class="brush: as3">package
{
	import flash.events.Event;

	public class LoopEvent extends Event
	{
		public static const START:String = "loopEventStart";
		public static const PROGRESS:String = "loopEventProgress";
		public static const COMPLETE:String = "loopEventComplete";

		private var _func:Function;
		private var _count:int;
		private var _total:int;

		public function LoopEvent($type:String, $func:Function, $count:int, $total:int)
		{
			_func = $func;
			_count = $count;
			_total = $total;
			super($type);
		}

		public function get count():int { return _count; }

		public function get func():Function { return _func; }

		public function get total():int { return _total; }

		override public function toString():String
		{
			return '[LoopEvent type="'+type+'" func="'+_func+'" count="'+_count+'" total="'+_total+'"';
		}
	}

}</pre>
<strong>Main.as</strong>
<pre class="brush: as3">package
{
	import flash.display.Sprite;
	import flash.events.Event;

	public class Main extends Sprite
	{

		public function Main():void
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point

			var looper:LoopShape = new LoopShape;
			looper.loop( 100, 10, trace_start);
			looper.loop( 100, 10, trace_progress);
			looper.loop( 100, 10, trace_complete);

			looper.addEventListener(LoopEvent.START, handleStart);
			looper.addEventListener(LoopEvent.PROGRESS, handleProgress);
			looper.addEventListener(LoopEvent.COMPLETE, handleComplete);
		}

		private function trace_start():void {
			//trace("trace_start");
		}

		private function trace_progress():void {
			//trace("trace_progress");
		}

		private function trace_complete():void {
			//trace("trace_complete");
		}

		private function handleStart(e:LoopEvent):void
		{
			if (e.func == trace_start) {
				trace(e);;
			}
		}

		private function handleProgress(e:LoopEvent):void
		{
			if (e.func == trace_progress) {
				trace(e);;
			}
		}

		private function handleComplete(e:LoopEvent):void
		{
			if (e.func == trace_complete) {
				trace(e);;
			}
		}
	}
}

과제의 요지는 본문의 nonBlockingLoop 클래스를 Shape 을 상속 하고 완료이벤트를 발생 할 수 있도록 하는 것 입니다. Shape 으로 간단히 상속해주고 ENTER_FRAME 이벤트를 등록합니다. 완료이벤트 발생을 위해 LoopEvent 라는 커스텀 이벤트를 하나 만들었습니다. 완료 이벤트는 단순히 Event.COMPLETE 를 쓸 수도 있겠습니다만 이럴 경우 어떤 함수에 대한 루프 이벤트 인지 판별 할 수 없기 때문에 해당 함수를 인자로 갖는 이벤트를 만들었습니다. 덤으로 count 와 total 도 넣어습니다. 이것으로 루프의 현재진행률도 알 수 있습니다.

이벤트를 매번 새로 생성하는건 상당한 자원 낭비라 LoopEvent 인스턴스를 하나 만들어 사용 할 까 했으나 그러기 위해서는 LoopEvent 의 func, count, total 속성에 쓰기를 허용해야 하므로 일단 매번 이벤트를 만드는 쪽으로 했습니다. 사실 어떤게 더 나은 방법인지는 아직도 잘 모르겠습니다.

11 thoughts on “Non blocking loop – homework”

  1. 아…몇가지 아쉬운 점이 있어요.
    1. _this는 무의미. 제거!
    2. 이 샘플은 잘 작동할 거 같지만 전혀 그렇지 않다는..왜냐면 루프는 runner에 대해 수행하면서 리스너는 별도로 관리하기 때문에 프로그래스 이벤트리스너는 여러 러너의 counter값을 무작위로 날리게 되요(잘 생각해보심^^)
    3. 같은 이유로 complete도 엉망이 됩니다.
    4. 원천적인 이유는 loop는 매번 클로저로 관리되는데 디스패치는 객체기준으로 작동하기 때문이죠.
    5. 이 모순을 해결해주삼!

    1. 1. 이벤트리스너에 function을 직접 넣을 경우 this 에 대한 scope 가 늘 헷갈리더라구요. 그래서 확실히 알아보려구 _this 를 사용했습니다.

      2. 여러 runner의 카운트값을 무작위로 날리기 때문에 그 해결책으로 넣은게 LoopEvent 의 func 속성이였습니다. 이걸로 비교해서 판단하는 단순무식한 방법ㅎㅎㅎ;; 근데 말씀하신것 처럼 루프와 이벤트리스너가 별도 관리 되는건 좀 모순이네요;;;; 더 좋은 해결책을 한번 고민해 봐야겠네요.

    1. 그냥 쉽게 해보려고 했는데 hika님의 역습~ㅠㅠ 어렵게 되었습니다. ㅎㅎㅎ 솔직히 어떻게 해야될지 모르겠다는…

  2. 아.. 알죠 물론 if (e.func == trace_complete) 이 코드의 의미를.
    요점은 그걸 안하게 하는거죠 ^^
    만약 this가 해깔린다면 사람이 해깔리는거고 컴파일러는 컨텍스트를 찾는 원칙으로 바르게 찾으니까 괜찮아요. 함수를 동적으로 생성하는 경우 클로저의 생성원칙에 의해 객체 참조를 자신을 감싸는 중괄호순으로 참조하게 되어있습니다.
    class a{
    function b(){
    var c= function(){this}
    }
    이 구조에서 c에 할당된 함수의 this는 중괄호를 거슬러가면 b함수의 중괄호가 먼저 나오는데 이건 함수지 객체가 아니므로 this를 매칭시킬 수 없으니 그다음 중괄호인 class a{로 이동합니다. 따라서 a의 인스턴스가 자동으로 this에 매칭되죠.

    1. ㅠㅠ 이런 또 어려운 과제를~~~ 하지만 과제는 어려워야 맛이지요~ㅎㅎㅎ 나름 고민해보겠습니다.

      그리고 객체 참조 원리를 답글로 한방에 알려주시다니…아~ 감동의 도가니탕~~ ㅠㅠ 알려 주시는김에 이런 원리에 대해 찾아 볼 수 있는 문서가 있을까요? 이참에 확실히 집고 넘어가보려고 합니다.

  3. 안녕하세요 , 이제서야 리플을 답니다 –;
    (사실 본업이 매우 엄청 갑자기 빢세져서요.)
    사실 이벤트를 날리건 콜백을쓰건 관점의 차이라고 생각합니다. 그런데 제가 히카님의 과제의도를 잘못 파악하긴 했습니다.
    코드를 보니 매우 깔끔하고 좋습니다. 예제도 올려주시고,.. 지금은 차분히 읽을 형편이 안되서 급하게 봤지만 역시 [파워플 멤버]답게 배울것이 많은 포스트 입니다. 앞으로도 많은 교류? 부탁드립니다 ^^

    1. 네 동감합니다. 그 유명한 TweenMax 도 콜백함수를 사용하니 콜백도 분명 좋은 방법지요. 절대 어떤게 정답이라는건 없다고 생각합니다. 제가 작성한 소스도 그냥 수 많은 방법들 중 하나이지요. 다만 어떻게 구현 하는게 좋을지는 늘 고민 입니다.

      바쁘시겠지만 자주 놀러오셔서 고견 주시면 감사하겠습니다. ^^

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.