如果對線程了解不夠清楚,在項目就使用線程,會給開發帶來很多問題。所以在iphone的項目中使用線程,最好先學習Apple的線程開發向導。以下是自己開發過程中,使用線程遇到的問題,以及解決的方法。
第一個問題,為什么要使用線程。編輯本段回目錄
在解決這個問題之前,要先了解什么是線程。在官方的線程開發文檔中有明確的定義:Threads are a relatively lightweight way to implement multiple paths of execution inside of an application.( 線程是一個相對輕量級的,在應用程序內部,實現執行的多條路徑的方式。)。通俗的講就是, 程序中的函數調用是一步一步執行的,而線程可以讓多個函數同時調用,根據內核程序的調度,在不同的時間執行不同的函數,結果就像是程序在執行的過程中同時開通了多條路徑,而如果沒有線程,程序只能走一條路徑。所以線程就自然有下面的兩個優點:
1)多線程可以提高應用程序的響應能力,也就是,有了多線程應用程序才能一邊在后台處理數據,一邊還能接受用戶的輸入響應。否則只能等數據處理完,才能接受用戶的輸入響應。
2)在多核系統中,多線程能夠提高應用程序的實時行動。因為多線程可以安排程序多條執行路徑,所以在多核環境下,每個核同時可以執行不同路徑的代碼,這樣就提高了應用程序的執行能力,同時也縮短了執行時間。
那么已經可以下結論了,之所以使用多線程,無非是2個原因。
1)為了使應用程序在處理數據的同時,不影響用戶對應用程序的正常操作,就要使用多線程。否則用戶只能等待數據處理完畢,這往往是人們無法忍受的。
2)如果應用程序中有大量的IO中斷(譬如文件的讀寫,網絡的訪問…),以及用戶的大量輸入,這樣處理器為了等待這些中斷,就會處於空閑狀態。而多線程恰恰可以利用了這些空閑狀態,讓應用程序做一些必要的計算,可以大大提升應用程序的執行效率。
網絡下載是程序設計中經常遇到的問題,根據前面的介紹,會發現這里就需要用到多線程。因為用戶在程序從網絡上下載數據的過程,不可能等待數據到達。那么就針對這個實例,來說明第二個問題如何使用線程。
1)多線程可以提高應用程序的響應能力,也就是,有了多線程應用程序才能一邊在后台處理數據,一邊還能接受用戶的輸入響應。否則只能等數據處理完,才能接受用戶的輸入響應。
2)在多核系統中,多線程能夠提高應用程序的實時行動。因為多線程可以安排程序多條執行路徑,所以在多核環境下,每個核同時可以執行不同路徑的代碼,這樣就提高了應用程序的執行能力,同時也縮短了執行時間。
那么已經可以下結論了,之所以使用多線程,無非是2個原因。
1)為了使應用程序在處理數據的同時,不影響用戶對應用程序的正常操作,就要使用多線程。否則用戶只能等待數據處理完畢,這往往是人們無法忍受的。
2)如果應用程序中有大量的IO中斷(譬如文件的讀寫,網絡的訪問…),以及用戶的大量輸入,這樣處理器為了等待這些中斷,就會處於空閑狀態。而多線程恰恰可以利用了這些空閑狀態,讓應用程序做一些必要的計算,可以大大提升應用程序的執行效率。
網絡下載是程序設計中經常遇到的問題,根據前面的介紹,會發現這里就需要用到多線程。因為用戶在程序從網絡上下載數據的過程,不可能等待數據到達。那么就針對這個實例,來說明第二個問題如何使用線程。
第二個問題如何使用線程。編輯本段回目錄
解決第二個問題之前,要引入一個概念
Run Loops。可能在做windows及linux線程開發的過程中,沒有聽過這個概念。所以這里非常有必要解釋以下,其實根據官方文檔的解釋,我們可以完全理解Run loop的用途,以及它和線程的關系。
A run loop is a piece of infrastructure used to manage events arriving asynchronously on a thread.( 一個Run loop是在線程中管理事件異步到達的基礎設備) 。這里就有一個問題了,什么是異步到達。這里有個例子可以形象的表述這個問題。假如你餓了想吃東西了,你可能會親自出去一趟,到某個便利店買點東西,然后吃掉。還有一個方法就是,你打電話給某個便利店幫你送一份外賣,這是你只需要在家里等外賣的人,通知你就可以了。第二個方法就是所謂的異步。所以轉到計算機程序設計里面,異步就是當主程序中執行某個方法的時候,主程序不需要知道這個方法什么時候執行完,而是這個方法執行完的時候通知主程序就可以了。
根據上面的介紹,你會發現在線程中,常常會有2中不同的方式執行線程中的代碼。第一個,把你要執行的所有的代碼都寫入到線程中,這樣自然寫入的代碼執行完畢,線程同時就結束了。而第二種方式 是你在線程中開啟了一個方法,這個方法可能是你開的另外一個線程,你不知道這個方法什么時候執行完,需要這個方法執行完時,通知你的主線程,那么主線程怎么知道什么時候應該結束那?這里就必須要用到run loop。Run loop正如他字面意思一樣,就是提供一個循環,就是當有異步輸入源的時候,就等待異步輸入源的結束。一個Run loop也就是當沒有事情要做的時候,它就使線程進入休眠,有事情做的時候就開啟線程。所以你完全可以自己寫代碼按照Run loop的思想,實現線程中的異步輸入源的控制。官方API也解釋了Run loop的使用要求:
You are not required to use a run loop with any threads you create
but doing so can provide a better experience for the user.
Run loops make it possible to create long-lived threads that use a minimal amount of resources.
Because a run loop puts its thread to sleep when there is nothing to do,
it eliminates the need for polling, which wastes CPU cycles and prevents the processor itself from sleeping
and saving power.( 你不必要求創建的每個線程都使用run loop,但是如果你這樣做了,能夠給使用者提供一個很好的經歷。Run loops使創建一個長期生存的線程而使用很少的資源成為可能性。因為當線程沒事可做的時候,一個run loop可以使線程休眠。同時,也取消了線程狀態切換的需求,減少了CPU時間片,同時放置處理器休眠,並且節約了能量) 。這里你就明白了為何蘋果的工程師要為線程提供一個Run loop那,就是為了減少線程在等待異步時間的時候,對資源的消耗。所以如果理解了Run loop自然線程如何使用就顯而易見了。下面的實例代碼就是線程如何處理異步輸入源的,以及Run loop在其中的作用:
這里Run loop是使用官方提供的core function中的函數實現的,而注釋掉的代碼是使用cocoa中的函數實現。如果要了解這兩種實現方法,可以在API文檔中找到詳細的例子,這里不再贅余。
A run loop is a piece of infrastructure used to manage events arriving asynchronously on a thread.( 一個Run loop是在線程中管理事件異步到達的基礎設備) 。這里就有一個問題了,什么是異步到達。這里有個例子可以形象的表述這個問題。假如你餓了想吃東西了,你可能會親自出去一趟,到某個便利店買點東西,然后吃掉。還有一個方法就是,你打電話給某個便利店幫你送一份外賣,這是你只需要在家里等外賣的人,通知你就可以了。第二個方法就是所謂的異步。所以轉到計算機程序設計里面,異步就是當主程序中執行某個方法的時候,主程序不需要知道這個方法什么時候執行完,而是這個方法執行完的時候通知主程序就可以了。
根據上面的介紹,你會發現在線程中,常常會有2中不同的方式執行線程中的代碼。第一個,把你要執行的所有的代碼都寫入到線程中,這樣自然寫入的代碼執行完畢,線程同時就結束了。而第二種方式 是你在線程中開啟了一個方法,這個方法可能是你開的另外一個線程,你不知道這個方法什么時候執行完,需要這個方法執行完時,通知你的主線程,那么主線程怎么知道什么時候應該結束那?這里就必須要用到run loop。Run loop正如他字面意思一樣,就是提供一個循環,就是當有異步輸入源的時候,就等待異步輸入源的結束。一個Run loop也就是當沒有事情要做的時候,它就使線程進入休眠,有事情做的時候就開啟線程。所以你完全可以自己寫代碼按照Run loop的思想,實現線程中的異步輸入源的控制。官方API也解釋了Run loop的使用要求:
You are not required to use a run loop with any threads you create
but doing so can provide a better experience for the user.
Run loops make it possible to create long-lived threads that use a minimal amount of resources.
Because a run loop puts its thread to sleep when there is nothing to do,
it eliminates the need for polling, which wastes CPU cycles and prevents the processor itself from sleeping
and saving power.( 你不必要求創建的每個線程都使用run loop,但是如果你這樣做了,能夠給使用者提供一個很好的經歷。Run loops使創建一個長期生存的線程而使用很少的資源成為可能性。因為當線程沒事可做的時候,一個run loop可以使線程休眠。同時,也取消了線程狀態切換的需求,減少了CPU時間片,同時放置處理器休眠,並且節約了能量) 。這里你就明白了為何蘋果的工程師要為線程提供一個Run loop那,就是為了減少線程在等待異步時間的時候,對資源的消耗。所以如果理解了Run loop自然線程如何使用就顯而易見了。下面的實例代碼就是線程如何處理異步輸入源的,以及Run loop在其中的作用:
- (void)asynchronousSource{ for (int i = 0; i < 100; i++) { NSLog(@"asynchronous input source run"); } [[NSThread currentThread] cancel]; } - (void)thread{ NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]init]; [self performSelector:@selector(asynchronousSource) withObject:nil afterDelay:1]; NSLog(@"me"); BOOL done = NO; do{ // Start the run loop but return after each source is handled. SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES); // If a source explicitly stopped the run loop, or if there are no // sources or timers, go ahead and exit. if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) done = YES; if ([_myThread isCancelled]) { done = YES; NSLog(@"thread exit"); } // Check for any other exit conditions here and set the // done variable as needed. } while (!done); // while (done) { // [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; // if ([_myThread isCancelled]) { // finish = NO; // NSLog(@"thread exit"); // } // } [pool release]; } |
這里Run loop是使用官方提供的core function中的函數實現的,而注釋掉的代碼是使用cocoa中的函數實現。如果要了解這兩種實現方法,可以在API文檔中找到詳細的例子,這里不再贅余。