iphone5s 出來了,CPU 升級到了64位 可以應用到更大的內存了,同時對多線程的處理吞吐能力也教之前提升了十幾倍。下面著中總結下ios 下多線程的知識點

1:現有的幾種多線程
概念 應用場景NSThread | 蘋果公司的Cocoa框架共支持三種多線程機制,分別為NSThread、GCD(Grand Central Dispatch)、Cocoa NSOperatio。NSThree是官方推薦的線程處理方式,它在處理機制上,需要開發者負責手動管理Thread的生命周期,包括子線程與主線程之間的同步等。線程共享同一應用程序的部分內存空間,它們擁有對數據相同的訪問權限。你得協調多個線程 對同一數據的訪問,一般做法是在訪問之前加鎖,這會導致一定的性能開銷。在 iOS 中我們可以使用多種形式的 thread。 比其他兩個輕量級 需要自己管理線程的生命周期,線程同步。 線程同步對數據的加鎖會有一定的系統開銷 |
NSOperation | 如果需要讓線程同時并行運行多個,可以將線程加入隊列(Queue)中,NSOperationQueue類就是一個線程隊列管理類,他提供了線程并行、隊列的管理。可以認為NSOperationQueue就是一個線程管理器,通過addOperations方法,我們可以一次性把多個(數組形式)線程添加到隊列中。同時,NSOperationQueue允許通過setMaxConcurrentOperationCount方法設置隊列的并行(同一時間)運行數量 |
GCD | Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。該方法在Mac OS X 10.6雪豹中首次推出,并隨后被引入到了iOS4.0中。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術,它看起來象就其它語言的閉包(Closure)一樣,但蘋果把它叫做blocks。 |
一般移動平臺上系統都會有一個專門的檢查機制,看程序有沒有很長時間被阻塞住,沒有回來檢查主消息隊列。發現這種情況一般都是把程 序作為“無響應”干掉。iOS一般情況下是10秒為上限。10秒內程序沒有回到主消息循環就被干掉。在前臺后臺切換時更嚴格,大概是5秒左右。 注釋1
2:簡單的Demo
因為GCD 是應用最廣的 而且也是蘋果現在極力鼓動開發者應用的 所以NSThread NSOperation 只做簡單應用
1:NSThread
1.1 NSThread 有兩種直接創建方式:
第一個是實例方法--直接創建線程并且開始運行線程
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)
第二個是類方法--先創建線程對象,然后再運行線程操作,在運行線程操作前可以設置線程的優先級等線程信息
- (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
selector
:線程執行的方法,這個selector只能有一個參數,而且不能有返回值。
target
:selector消息發送的對象
argument
:傳輸給target的唯一參數,也可以是nil
1.2線程間通信
a 在應用程序主線程中做事情:
performSelectorOnMainThread: withObject: waitUntilDone: performSelectorOnMainThread: withObject: waitUntilDone: modes:
b 在指定線程中做事情:
performSelector: onThread: withObject: waitUntilDone: performSelector: onThread: withObject: waitUntilDone: modes:
c 在當前線程中做事情:
performSelector: withObject: afterDelay: performSelector: withObject: afterDelay: inModes:
d 取消發送給當前線程的某個消息
cancelPreviousPerformRequestsWithTarget: cancelPreviousPerformRequestsWithTarget: selector: object:
2:NSOperation
首先是建立NSOperationQueue和NSOperations。NSOperationQueue會建立一個線程管理器,每個加入到線程operation會有序的執行。
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation =
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doWork:) object:someObject];
[queue addObject:operation];
[operation release];
使用NSOperationQueue的過程:
1. 建立一個NSOperationQueue的對象
2. 建立一個NSOperation的對象
3. 將operation加入到NSOperationQueue中
4. release掉operation
NSInvocationOperation,NSInvocationOperation是NSOperation的子類,允許運行在operation中的targer和selector
3:多線程互斥同步問題
在iOS中有幾種方法來解決多線程訪問同一個內存地址的互斥同步問題:
- 方法一,@synchronized(id anObject),(最簡單的方法) 會自動對參數對象加鎖,保證臨界區內的代碼線程安全
@ synchronized ( self ) { // 這段代碼對其他 @synchronized(self) 都是互斥的 // self 指向同一個對象 }
- 方法二,NSLock
NSLock對象實現了NSLocking protocol,包含幾個方法: lock,加鎖 unlock,解鎖 tryLock,嘗試加鎖,如果失敗了,并不會阻塞線程,只是立即返回NO lockBeforeDate:,在指定的date之前暫時阻塞線程(如果沒有獲取鎖的話),如果到期還沒有獲取鎖,則線程被喚醒,函數立即返回NO 比如 : NSLock *theLock = [[NSLock alloc] init];
if ([the Lock lock ]) { // do something here [theLock unlock]; }
- 方法三,NSRecursiveLock,遞歸鎖
NSRecursiveLock,多次調用不會阻塞已獲取該鎖的線程。
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init]; void MyRecursiveFunction(int value) { [theLock lock]; if (value != 0) { –value; MyRecursiveFunction(value); } [theLock unlock]; } MyRecursiveFunction(5);
- 方法四,NSConditionLock,條件鎖
NSConditionLock,條件鎖,可以設置條件
//公共部分 id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA]; //線程一,生產者 while(true) { [condLock lockWhenCondition:NO_DATA]; //生產數據 [condLock unlockWithCondition:HAS_DATA]; } //線程二,消費者 while (true) { [condLock lockWhenCondition:HAS_DATA //消費 [condLock unlockWithCondition:NO_DATA]; }
- 方法五,NSDistributedLock,分布鎖
NSDistributedLock,分布鎖,文件方式實現,可以跨進程 用tryLock方法獲取鎖。 用unlock方法釋放鎖。 如果一個獲取鎖的進程在釋放鎖之前掛了,那么鎖就一直得不到釋放了,此時可以通過breakLock強行獲取鎖。
本章節(多線程互斥同步問題)參考自: http://blog.sina.com.cn/s/blog_72819b170101590n.html
3:GCD多線程互斥同步問題(阻塞線程的方式去實現同步)
1.串行隊列
(1)GCD下的dispatch_queue隊列都是FIFO隊列,都會按照提交到隊列的順序執行. 只是根據隊列的性質,分為
<1>串行隊列:用戶隊列、主線程隊列
<2>并行隊列.
(2)同步(dispatch_sync)、異步方式(dispatch_async). 配合串行隊列和并行隊列使用.
同步隊列直接提交兩個任務就可以. // 串形隊列 dispatch_queue_t serilQueue = dispatch_queue_create("com.quains.myQueue", 0);
//開始時間 NSDate *startTime = [ NSDate date]; __block UIImage *image = nil ; //1.先去網上下載圖片 dispatch_async (serilQueue, ^{ //下載圖片 }); //2.在主線程展示到界面里 dispatch_async (serilQueue, ^{ NSLog (@ "%@" ,[ NSThread currentThread]); // 在主線程展示 dispatch_async (dispatch_get_main_queue(), ^{ //顯示圖片 }); //3.清理 dispatch_release(serilQueue); [image release];
注意:
(1) __block變量分配在棧,retain下,防止被回收.
(2)dispatch要手動create和release.
(3)提交到主線程隊列的時候,慎用同步dispatch_sync方法,有可能造成死鎖. 因為主線程隊列是串行隊列,要等隊列里的任務一個一個執行.所以提交一個任務到隊列,如果用同步方法就會阻塞住主線程,而主線程又要等主線程隊列里的任務都執行完才能執行那個剛提交的,所以主線程隊列里還有其他的任務的話,但他已經被阻塞住了,沒法先完成隊列里的其他任務,即,最后一個任務也沒機會執行到,于是造成死鎖.
(4)提交到串行隊列可以用同步方式,也可以用異步方式.
2.并行隊列
采用并行隊列的時候,可以采用同步的方式把任務提交到隊列里去,即可以實現同步的方式
//新建一個隊列 dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//記時 NSDate *startTime = [ NSDate date]; //加入隊列 dispatch_async (concurrentQueue, ^{ __block UIImage *image = nil ; //1.先去網上下載圖片 dispatch_sync (concurrentQueue, ^{ //下載圖片 }); //2.在主線程展示到界面里 dispatch_sync (dispatch_get_main_queue(), ^{ //顯示圖片 }); });
兩個同步的任務用一個異步的包起來,提交到并行隊列里去,即可實現同步的方式.
3.使用分組方式
group本身是將幾個有關聯的任務組合起來,然后提供給開發者一個知道這個group結束的點. 雖然這個只有一個任務,但是可以利用group的結束點,去阻塞線程,從而來實現同步方式.
例如讓后臺2個線程并行執行,然后等2個線程都結束后,再匯總執行結果。這個可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify來實現,示例如下:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async( group , dispatch_get_global_queue( 0 , 0 ), ^{ // 并行執行的線程一 }); dispatch_group_async( group , dispatch_get_global_queue( 0 , 0 ), ^{ // 并行執行的線程二 }); dispatch_group_notify( group , dispatch_get_global_queue( 0 , 0 ), ^{ // 匯總結果 });
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue( 0 , 0 ); NSDate *startTime = [NSDate date ]; __block UIImage *image = nil; dispatch_group_async( group , queue, ^{ //1.先去網上下載圖片 }); // 2.等下載好了再在刷新主線程 dispatch_group_notify( group , queue, ^{ //在主線程展示到界面里 dispatch_async(dispatch_get_main_queue(), ^{ //顯示圖片 }); }); // 釋放掉 dispatch_release( group );
dispatch_group 也要手動創建和釋放. dispatch_notify()提供了一個知道group什么時候結束的點. 當然也可以使用dispatch_wait()去阻塞
4.信號量
信號量 和 瑣 的作用差不多,可以用來實現同步的方式. 但是信號量通常用在 允許幾個線程同時訪問一個資源,通過信號量來控制訪問的線程個數.
// 信號量初始化為1 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue( 0 , 0 ); NSDate *startTime = [ NSDate date]; __block UIImage *image = nil ; //1.先去網上下載圖片 dispatch_async (queue, ^{ // wait操作-1 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 開始下載 // signal操作+1 dispatch_semaphore_signal(semaphore); }); // 2.等下載好了再在刷新主線程 dispatch_async (dispatch_get_main_queue(), ^{ // wait操作-1 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //顯示圖片 // signal操作+1 dispatch_semaphore_signal(semaphore); });
dispatch_wait會阻塞線程并且檢測信號量的值,直到信號量值大于0才會開始往下執行,同時對信號量執行-1操作.
dispatch_signal則是+1操作.
3.后臺運行
GCD的另一個用處是可以讓程序在后臺較長久的運行。在沒有使用GCD時,當app被按home鍵退出后,app僅有最多5秒鐘的時候做一些保存或清理資源的工作。但是在使用GCD后,app最多有10分鐘的時間在后臺長久運行。這個時間可以用來做清理本地緩存,發送統計數據等工作。
讓程序在后臺長久運行的示例代碼如下:
// AppDelegate.h文件 @ property ( assign , nonatomic ) UIBackgroundTaskIdentifier backgroundUpdateTask; // AppDelegate.m文件 - ( void )applicationDidEnterBackground:( UIApplication *)application { [ self beingBackgroundUpdateTask]; // 在這里加上你需要長久運行的代碼 [ self endBackgroundUpdateTask]; } - ( void )beingBackgroundUpdateTask { self .backgroundUpdateTask = [[ UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [ self endBackgroundUpdateTask]; }]; } - ( void )endBackgroundUpdateTask { [[ UIApplication sharedApplication] endBackgroundTask: self .backgroundUpdateTask ]; self .backgroundUpdateTask = UIBackgroundTaskInvalid; }
[1] http://www.cnblogs.com/linyawen/archive/2012/07/24/2606709.html
[2] http://www.cnblogs.com/Quains/archive/2013/07/10/3182823.html
[3] http://blog.devtang.com/blog/2012/02/22/use-gcd/
另外 本人打算十一后離職換份工作,哪位仁兄公司有意向 站內信聯系,幫忙內推下哈.
再打下廣告哈 我搭建的個人博客 也已經上線了 引用的是hexo , 地址是 hufeng825.github.com ui配色再微調中,對ie10 以下支持不太好 建議用chrome Firefox 或者safrari 瀏覽 另外也已經對移動設備做了響應是布局.
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
