精華區beta MacDev 關於我們 聯絡資訊
※ 引述《Dannier (貓尾巴~)》之銘言: : 我想要程式按一個butten可以開始持續做某件事 按另一個butten可以結束這件事 : 可是我發現按了第一個butten之後我就不能按第二個了 : 我大概知道是因為我的程式架構有問題 可是我實在是想不出為什麼 看來你沒有把多緒程式的概念弄清楚,也不了解一般GUI程式的做法。 基本的程式都是循序執行: main() //程式開始 { 指令1; 指令2; ... 指令n; return result; //程式結束,回傳執行結果 } 這種基本的循序程式,都是執行一次就直接結束。 但比較實際的程式,通常都要與使用者互動,若是執行一次就結束,就無法 與使用者互動,做不同的反應。 所以會有像下面這樣的程式: main() { while(1) { 使用者輸入; switch(輸入值) { case A: //如果使用者輸入A doA(); case B: doB(); ...//中略 } } } 而到了多工的程式中,如GUI程式,除了要與使用者互動外,還必需把執行的時間分配給 不同的執行緒,以便做畫面的更新,或是檔案存取,IO等動作。 這時程式的架構會像下面: main() { while(1) //假設跑一次迴圈要10s { updateDisplay(); //畫面更新 -- 1s getUIAction(); //擷取使用者輸入或其他事件 -- 1s dispatchTask(); //根據所擷取到的事件,分配工作 -- 1s ... // 其他各類工作 -- 總共7s } } 而NSRunLoop所做的就是將上一段的while(1)包成一個class供我們使用. 現在回到你的程式 : ---------------------------------------------------------------------- : /* periodicTest.m*/ : #import "periodicTest.h" : @implementation periodicTest : - (void)doloop:(BOOL)R{ : double resolution = 1.0; : while (R) : { : NSDate* next = [NSDate dateWithTimeIntervalSinceNow:resolution]; : [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:next]; : printf("Do Loop...\n"); : } : } : @end 這段程式有好幾個問題: 1. NSRunLoop在這裡根本看不出有任何的功用. 一般NSRunLoop的用法,都是用在另一個thread,或是當我有一個新的持續性的工作時, 我們會去向NSRunLoop註冊,告訴他,我們有一個新的工作,請NSRunloop幫我們安排時 間去執行(NSTimer就是一例). 但是在這裡,你既沒有產生另一個thread,也沒有安排工作給NSRunLoop,你只是告訴 Current Loop,執行一次就停下來,而你的current loop看來就是main loop. 如果不是 加了外面的while(R),恐怕整個程式就掛掉了. 2. 你的程式沒有停止的條件。 你想的是call doloop(YES) 就開始跑,call doloop(NO)就停,但是, - (void)doloop:(BOOL)R這個函式中, 「R」是一個區域變數,不是全域變數。 這意思是,你在呼叫doloop(YES)的時候的「R」與doloop(NO)的時候的「R」不是 同一個. 3. 因為你沒有生成一個新的Thread,所以你的程式真正跑起來的樣子類似: main loop { updateDisplay(); ... 中略 if actionA() { doLoop(YES); //在這裡會陷入迴圈. } ...其他工作 if actionB() { doLoop(NO); //應該永遠到不了這裡. } } 所以,如果你要解決目前的困境, 請再看看下面的文章: 1. http://0rz.tw/fe41v Threading Programming Guide 2. http://0rz.tw/c240q Timers 3. http://0rz.tw/b23Zk Run Loop 詳細的做法,請自己再想想. -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 219.84.219.98 ※ 編輯: atst 來自: 219.84.219.98 (05/05 20:18)
andrew43:好文! 05/05 21:03
> -------------------------------------------------------------------------- < 作者: icecicada (...) 看板: MacDev 標題: Re: [問題] 請問Cocoa程式要如何一直做同件事情? 時間: Sun May 11 22:11:10 2008 ※ 引述《Dannier (貓尾巴~)》之銘言: : 不好意思小弟不才 : 研究了一下了解了一些NSRunLoop : 可是還是有問題 : 我想要按一個扭開始一直做某件事情 : 按另一個則停止 : 但我還是不知道要怎麼讓它停止NSRunLoop : 實在想不出辦法來 : 想要請教高手指點....感激不盡(躬) 當AP被執行後,AP會自動執行一個NSRunLoop 這個RunLoop是不可以被停止的... 依照你的Code來看,你只是增加了一個Timer,然後把它加入了RunLoop中... RunLoop依照你的Timer設定值及Mode,決定何時呼叫你註冊的Function。 要停掉Timer,參見NSTimer的文件,- (void)invalidate就是拿來停掉Timer的.. 至於要不要新增一個Thread,要看你想要做的事來判斷... 如果只是定期執行,而且動作不是很複雜,可以用NSTimer解決, 不需要新增一個thread 如果是一直在執行,或是執行需要花費較長的時間,還是使用Theard會比較好... 因為一個thread再一個時間點只能做一件事... 如果所需要執行的時間過久,那在這個時間點UI就會被Lock,無法接受其它動作... -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 117.81.128.202 > -------------------------------------------------------------------------- < 作者: atst (電腦無法阻止人類做蠢事) 看板: MacDev 標題: Re: [問題] 請問Cocoa程式要如何一直做同件事情? 時間: Mon May 12 19:30:20 2008 ※ 引述《Dannier (貓尾巴~)》之銘言: : 不好意思小弟不才 : 研究了一下了解了一些NSRunLoop : 可是還是有問題 : 我想要按一個扭開始一直做某件事情 : 按另一個則停止 : 但我還是不知道要怎麼讓它停止NSRunLoop : 實在想不出辦法來 : 想要請教高手指點....感激不盡(躬) 把你的code刪掉了 同樣的事,我會用下面的做法: //Do it by Timer @interface MyObject: NSObject { NSTimer* myTimer; } - (IBAction)startTimer:(id)sender; - (IBAction)stopTimer:(id)sender; - (void)timerHandler:(NSTimer*)theTimer; - (void)myTask; @end @implementation MyObject - (IBAction)startTimer:(id)sender { //pseudo code //Start timer with repeat } - (IBAction)stopTimer:(id)sender { //Stop timer } - (void)timerHandler:(NSTimer*)theTimer { //do something before myTask if needed //call myTask //do something after myTask if needed } - (void)myTask { // What your want to repeat. } @end 或者是 //Do it by thread @interface MyObject: NSObject { BOOL isThreadRunning; NSLock* threadStateLock; } - (IBAction)startThread:(id)sender; - (IBAction)stopThread:(id)sender; - (void)myThread:(id)anObject; - (void)myTask; @end @implementation MyObject - (IBAction)startThread:(id)sender { //lock for changing thread state //set isThreadRunning YES //unlock //Start thread with function myThread. } - (IBAction)stopThread:(id)sender { //lock for changing thread state //set isThreadRunning NO //unlock } - (void)myThread:(id)anObject { //Declaring a local variable __isThreadRunning == NO //Do followings //{ // 1. do something before myTask if needed // 2. myTask // 3. do something after myTask if needed // 4. lock for loading thread state // __isThreadRunning = isThreadRunning // unlock //}while(__isThreadRunning == YES) } - (void)myTask { // What your want to repeat. } @end 我的原則是,儘量解說原理,而不提供實際的程式。 在我之前的文章中,已經提到過,NSRunLoop在使用上有一些條件。 我所沒有明確的提出的是,你的需求並不適宜使用NSRunLoop。 關於這點,icecicada的文章中說得很清楚,你所需要使用的是NSThread或NSTimer。 在這裡,我再解釋得清楚一點: 1. 執行緒與Run Loop是不同的兩回事。 所謂的多緒,指的是「同時進行」的工作。在多核心,或是多處理器的環境下, 要達到「同時進行」的目的,可將不同的執行緒,交由不同的核心去處理如下: T1(task 1) T2(task2) | | | | ↓ ↓ 而RunLoop呢,其實是用來實現所謂的「分時多工」,其動作如下: T1 |←────┐ |(task 1) | | | |(task 2) | | | └─────┘ 在過去單核單處理器的情形下,因為多緒的「同時進行」一事,也是用「分時多工」的 方式去達成,所以有時會比較容易搞混。 2. NSRunLoop本質上雖然是一個迴圈,但與while(),for()的意義及使用目的不同。 如前所述,NSRunLoop是用來實現「分時多工」的,所以裡面除了「重複」外, 還會做所謂的Dispatch,也就是「工作分配」。 但while() 及for()這種基礎的迴圈,就只是很單純的「重複」。 3. 回到你的需求,你希望 a.重複做某件事 b.當滿足某條件時,停止a. 你可以很清楚的看到,你的需求a,只有「重複」的要求而已,而不需要「分配工作」。 因此你根本不需要用到NSRunLoop來幫你做事。 至於需求b,則可以用「分時」的方式,或是「多緒」的方式擇一來達成。 也就是在這裡,你可能搞混了,誤以為你的需求 a+b 一定要存取到NSRunLoop。 多緒的方式,上面的虛擬碼應該已經很清楚,我這邊講解一下「分時」的做法。 首先,如icecicada所述,在Cocoa應用程式執行時,其實已經有一個NSRunLoop在跑了。 它看起來可能像是下面這樣: Main Thread |←───────┐ |(do GUI task) | |(get event) | |(dispatch event)| |(blahblah...) | └────────┘ 我們可以看到,在這個Run Loop中,其實已經有許多重複的工作了, 這些重複的工作,負責維護應用程式的各種狀態, 並處理使用者對應用程式所做的各類動作。 而當你想要「重複」某件工作時,在597篇你做的事是: Main Thread |←───────┐ |(do GUI task) | |(get event) | ┌┤(dispatch event)|(blahblah...) | └────────┘ └→ 1.如果startEvent: 1.1 doLoop:(進入迴圈) 1.1.1 do something 因為在1.1,你進入了一個迴圈,造成Main thread一直停在一個副迴圈中, 所以無法再繼續進行get event, dispatch event,do other things的流程。 在分時的情況下,你真正應該做的事是像下面這樣: Main Thread |←───────┐ |(do GUI task) | |(get event) | |(dispatch event)| |(blahblah...) | |1.1.1 do something (將1.1.1加入Run Loop) └────────┘ 這也是你在上一篇所試著要做的。 但是,雖然你試著要將一個新工作加入Run Loop, 但因為你沒有弄清楚NSTimer與NSRunLoop的角色,所以會寫出一些多餘的程式。 當你在產生一個NSTimer,並將之與某個Selector結合時, 其實就已經幫你做好上面所描述的事情了,所有與NSRunLoop相關的操作, 都已經在NSTimer的實作中幫你做完了,你不需要自己再去做一次。 你要做的只有: 1. 產生一個NSTimer,並指定要做的事情(Selector) 2. 起動(fire)所產生的Timer 在2.的時候,NSTimer就會自動幫你將Selector加入Current Run Loop。 而當你呼叫[NSTimer invalidate]時,NSTimer就會幫你把方才加入的Selector, 移出Current Run Loop. 從上面,你也可以看出,為何icecicada會說: 「如果是一直在執行,或是執行需要花費較長的時間,還是使用Theard會比較好...」 因為你還是在同一個Thread中,而不是不同的Thread。 而使用多緒的方式,執行起來會像下面: Main Thread child |←───────┐ |←───────┐ |(do GUI task) | |(do 1.1.1) | |(get event) | └───┬────┘ |(dispatch event)| | |(blahblah...) | | └───────┬┘ | └────→A←─┘ 這時候要讓child停下來,就得有一個變數A,能夠同時讓Main Thread與child存取。 這樣你才能在Main Thread中改變A的狀態, 而在Child中檢查A的狀態,並決定要不要停下來。 至於更詳細的class用法,還是請你去查一下NSTimer,NSThread的文件, 並參考一下別人的範例。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 219.84.252.229
Dannier:感謝你 真的是說明的很清楚 我今天自己有利用新產生一個 05/12 20:36
Dannier:Thread然後加工作到裡面結果可以執行了 不過我覺得我還是 05/12 20:37
Dannier:有多寫了一些步驟 我會再多用清楚一點的 謝謝感激不盡! 05/12 20:40