一、多線程的由來
二、耗時操做的模擬試驗
三、進程和線程
四、多線程的概念及原理
五、多線程的優缺點和一個Tip
六、主線程
七、技術方案
2、Pthread
---
一、函數
二、參數和返回值
三、使用
3、NSThread
---
一、建立一個新的線程
二、線程的狀態
三、線程的屬性
4、互斥鎖
---
一、訪問共享資源引入問題!
二、互斥鎖介紹
三、互斥鎖原理
四、互斥鎖和自旋鎖
5、GCD
---
一、GCD介紹
二、GCD的兩個核心
三、函數
四、串行隊列和併發隊列
五、主隊列
六、全局隊列
七、GCD總結
6、NSOperation
---
一、NSOperation簡介
二、核心概念
三、操做步驟
四、NSInvocationOperation
五、NSBlockOperation
7、案例
---
***ios
一個進程(進程)在執行一個線程(線程中有不少函數或方法(後面簡稱Function))的時候,其中有一個Function執行的時候須要消耗一些時間,可是這個線程又必須同時執行這個Function以後的Function,問題來了,一個線程中的任何一個Function都必須等待其執行完成後才能執行後面的Function,若是要同時執行兩個或者多個Function,那麼,就必須多開一個或者多個線程,這就是多線程的產生。我想多線程最開始的誕生就是由這而來吧!程序員
代碼算法
int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"bengin"); for (int i = 0; i < 10000000; i++) { } NSLog(@"end"); } return 0; }
控制檯編程
2016-02-16 13:51:54.140 Test[1670:603696] bengin 2016-02-16 13:51:54.160 Test[1670:603696] end Program ended with exit code: 0
結論一:循環一億次耗時0.02秒,計算機的運行速度是很是快的
安全
代碼多線程
int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"bengin"); for (int i = 0; i < 10000000; i++) { int n = 1; } NSLog(@"end"); } return 0; }
控制檯併發
2016-02-16 13:57:37.589 Test[1734:631377] bengin 2016-02-16 13:57:37.612 Test[1734:631377] end Program ended with exit code: 0
結論二:對棧區操做一億次,耗時0.023秒
框架
代碼:異步
int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"bengin"); for (int i = 0; i < 10000000; i++) { NSString *str = @"hellow"; } NSLog(@"end"); } return 0; }
控制檯async
2016-02-16 14:03:59.003 Test[1763:659287] bengin 2016-02-16 14:03:59.113 Test[1763:659287] end Program ended with exit code: 0
結論三:對常量區操做一億次,耗時0.11秒
代碼
int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"bengin"); for (int i = 0; i < 10000000; i++) { NSString *str = [NSString stringWithFormat:@"%d",i]; } NSLog(@"end"); } return 0; }
控制檯
2016-02-16 14:09:03.673 Test[1786:673719] bengin 2016-02-16 14:09:10.705 Test[1786:673719] end Program ended with exit code: 0
結論四:對堆區操做一億次耗時7秒多一些,較慢!
代碼
int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"bengin"); for (int i = 0; i < 10000000; i++) { NSLog(@"%d",i); } NSLog(@"end"); } return 0; }
控制檯輸出!正在跑中,一億次!!!先看截圖
CPU
再看內存
好吧,還在跑,如今已經達到10分鐘了,怕心疼本本炸掉!stop。。。
結論五:I/O操做很是慢,一億次10分鐘也沒能跑完!
最終結論:經過以上結論1、2、3、4、五得出一個結論,各個區的執行效率:棧區>常量區>堆區>I/O操做。同時也說明了一個問題,執行不一樣的方法會產什麼耗時操做。這是,爲了解決耗時操做問題,多線程閃亮誕生!
先說說進程和線程吧!
系統中正在運行的應用程序。
每一個進程都運行在其專用且受保護的內存空間,不一樣的進程之間相互獨立,互不干擾。
線程是進程的執行任務的基本單元,一個進程的全部任務都是在線程中執行的。(每個進程至少要有一條線程)。
線程在執行任務的時候是按順序執行的。若是要讓一條線程執行多個任務,那麼只能一個一個地而且按順序執行這些任務。也就是說,在同一時間,一條線程只能執行一個任務。
咱們能夠經過Mac中的活動監視器查看進程和線程,下圖!
1個進程能夠開啓多條線程,多條線程能夠同時執行不一樣的任務。
前提是在單核CPU的狀況下,同一時間,CPU只能處理一條線程,也就是說只有一條線程在執行任務。多線程同時執行,那是不可能的!可是是CPU快速地在多條線程之間進行調度和切換執行任務。若是CPU調度線程的速度足夠快,就會形成多條線程同時執行任務的」假象」,這種假象,就被美譽爲:多線程!
能夠適當提升程序的執行效率
也能夠適當提升資源的利用率(CPU、內存利用率)
開啓一條線程須要佔用必定的內存空間(默認狀況下,每一條線程都佔用512KB),若是開啓大量的線程,會佔用大量的內存空間,從而下降程序的性能。
線程越多,CPU在調度和切換線程上的開銷就會越大。
線程數越多,程序的設計會越複雜。
開啓新的線程就會消耗資源,可是卻能夠提升用戶體驗。在保證良好的用戶體驗的前提下,能夠適當地開線程,通常開3-6條。
開啓一條新的線程,默認狀況下,一條線程都是佔用512KB,可是官方的文檔裏面給出的說明卻不是,爲了得出真相,下面作個小小的測試!
代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
/** 操做主線程 /
NSLog(@"主線程默認 %tu", [NSThread currentThread].stackSize / 1024);
// 設置主線程的stackSize
[NSThread currentThread].stackSize = 1024 1024;
NSLog(@"主線程修改 %tu", [NSThread currentThread].stackSize / 1024);
/** 操做子線程 */ NSThread *thread = [[NSThread alloc] init]; NSLog(@"thread默認 %tu", thread.stackSize / 1024); // 設置子線程的stackSize thread.stackSize = 8 * 1024; NSLog(@"thread修改 %tu", thread.stackSize / 1024); [thread start];
}
return 0;
}
控制檯
2016-02-17 08:36:02.652 Test[609:110129] 主線程默認 512
2016-02-17 08:36:02.654 Test[609:110129] 主線程修改 1024
2016-02-17 08:36:02.654 Test[609:110129] thread默認 512
2016-02-17 08:36:02.654 Test[609:110129] thread修改 8
結論七:證實了,無論什麼線程,默認都是512,最小爲8.多是官方文檔沒有及時更新吧!
一個應用程序在啓動運行後,系統會自動開啓1條線程,這條稱爲」主線程」。
主線程上不能執行耗時操做,這樣會形成界面卡頓,給用戶一種很差的體驗。
***
pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict)
void *restrict 要執行的函數的參數
返回值 int類型 0是成功 非0 是失敗
代碼
#import <Foundation/Foundation.h> #import <pthread/pthread.h> void *demo(void *param) { NSString *name = (__bridge NSString *)(param); NSLog(@"hello %@ %@",name,[NSThread currentThread]); return NULL; } int main(int argc, const char * argv[]) { @autoreleasepool { //建立子線程 pthread_t pthread; //線程編號 NSString *test = @"test"; int result = pthread_create(&pthread, NULL, demo, (__bridge void *)(test)); NSLog(@"Began %@",[NSThread currentThread]); if (result == 0) { NSLog(@"成功"); }else { NSLog(@"失敗"); } } return 0; }
控制檯
2016-02-16 22:00:57.401 Test[888:42585] Began <NSThread: 0x100502d70>{number = 1, name = main} 2016-02-16 22:00:57.403 Test[888:42615] hello test <NSThread: 0x100102a30>{number = 2, name = (null)} 2016-02-16 22:00:57.403 Test[888:42585] 成功
__bridge 橋接,把OC中的對象,傳遞給c語言的函數,使用__bridge
***
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[NSThread detachNewThreadSelector:@selector(demo) toTarget:self withObject:nil];
[self performSelectorInBackground:@selector(demo) withObject:nil];
運行 Running
- (void)start;
阻塞(暫停) Blocked
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti;
死亡 Dead
+ (void)exit;
線程有兩個重要的屬性:名稱和優先級
設置線程名用於記錄線程,在出現異常時能夠DeBug
優先級或者服務質量高的,能夠優先調用,只是說會優先調用,可是不是百分之百的優先調用,這裏存在一個機率問題,內核裏的算法調度線程的時候,只是把優先級做爲一個考慮因素,還有不少個因數會決定哪一個線程優先調用。這點得注意注意!!!
下面是測試代碼
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //新建狀態 NSThread *test1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil]; test1.name = @"test1"; //線程的優先級 test1.threadPriority = 1.0; //就緒狀態 [test1 start]; //新建狀態 NSThread *test2= [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil]; test2.name = @"test2"; test2.threadPriority = 0; //就緒狀態 [test2 start]; } //線程執行完成以後會自動銷燬 - (void)demo { for (int i = 0; i < 20; i++) { NSLog(@"%d--%@",i,[NSThread currentThread]); } }
控制檯
2016-02-16 22:43:28.182 05-線程狀態[1241:78688] 0--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.182 05-線程狀態[1241:78689] 0--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.182 05-線程狀態[1241:78688] 1--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.182 05-線程狀態[1241:78688] 2--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.182 05-線程狀態[1241:78689] 1--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 3--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.183 05-線程狀態[1241:78689] 2--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 4--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 5--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.183 05-線程狀態[1241:78689] 3--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 6--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 7--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.183 05-線程狀態[1241:78689] 4--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.183 05-線程狀態[1241:78688] 8--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 9--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 10--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78689] 5--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 11--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78689] 6--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 12--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 13--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78689] 7--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 14--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78688] 15--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.185 05-線程狀態[1241:78688] 16--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.184 05-線程狀態[1241:78689] 8--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.185 05-線程狀態[1241:78688] 17--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.185 05-線程狀態[1241:78688] 18--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.185 05-線程狀態[1241:78689] 9--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.185 05-線程狀態[1241:78688] 19--<NSThread: 0x7fead2017f30>{number = 2, name = test1} 2016-02-16 22:43:28.185 05-線程狀態[1241:78689] 10--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.186 05-線程狀態[1241:78689] 11--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.186 05-線程狀態[1241:78689] 12--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.186 05-線程狀態[1241:78689] 13--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.186 05-線程狀態[1241:78689] 14--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.187 05-線程狀態[1241:78689] 15--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.187 05-線程狀態[1241:78689] 16--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.187 05-線程狀態[1241:78689] 17--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.187 05-線程狀態[1241:78689] 18--<NSThread: 0x7fead050a250>{number = 3, name = test2} 2016-02-16 22:43:28.187 05-線程狀態[1241:78689] 19--<NSThread: 0x7fead050a250>{number = 3, name = test2}
結論六:優先級高,不必定先執行,只能說明先執行的機率要大一些!!!
結論六
得出服務質量和優先級不能決定線程執行的前後順序,那麼問題來了,一個線程對共享資源作了修改,而另一個線程拿到的是未被修改以前資源,這是這個線程也對該資源作了修改,如今請問,兩個線程都對該資源作了不一樣的修改,那麼這個修改應該算誰的?!?!這就是問題所在!!!
這個文檔裏盜的圖!
解決方案很簡單,就是用一把鎖鎖住共享資源,等待線程1對其操做完畢後再打開,讓線程2來執行,這就是傳說中的互斥鎖
!!!@synchronized(鎖對象) { 須要鎖定的代碼 }
自旋鎖就是atomic!
若是發現其它線程正在執行鎖定代碼,線程會進入休眠(阻塞狀態),等其它線程時間片到了打開鎖後,線程就會被喚醒(執行)。
若是發現有其它線程正在執行鎖定代碼,線程會以死循環的方式,一直等待鎖定的代碼執行完成。
***
隊列的類型
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
// 1. 獲取全局隊列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 2. 建立任務 dispatch_block_t task = ^ { NSLog(@"hello %@", [NSThread currentThread]); }; // 3. 將任務添加到隊列,而且指定執行任務的函數 dispatch_async(queue, task);
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"hello %@", [NSThread currentThread]); });
先進先出,按照順序執行,而且一次只能調用一個任務
不管隊列中所指定的執行任務的函數是同步仍是異步,都必須等待前一個任務執行完畢才能夠調用後面的人
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("test", NULL);
代碼:
// 一、建立串行隊列 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); // 二、將任務添加到隊列,而且指定同步執行 for (int i = 0; i < 10; i++) { dispatch_sync(queue, ^{ NSLog(@"%@--%d",[NSThread currentThread],i); }); }
打印結果:
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--0
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--1
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--2
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--3
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--4
2016-02-25 16:31:07.849 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--5
2016-02-25 16:31:07.850 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--6
2016-02-25 16:31:07.850 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--7
2016-02-25 16:31:07.850 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--8
2016-02-25 16:31:07.850 test[1924:376468] <NSThread: 0x7ff040404370>{number = 1, name = main}--9
結論:串行隊列,同步執行,不開新線程,按順序執行
代碼:
// 一、建立串行隊列 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); // 二、將任務添加到隊列,而且指定同步執行 for (int i = 0; i < 10; i++) { dispatch_async(queue, ^{ NSLog(@"%@--%d",[NSThread currentThread],i); }); }
打印結果:
2016-02-25 17:08:32.167 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--0
2016-02-25 17:08:32.168 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--1
2016-02-25 17:08:32.168 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--2
2016-02-25 17:08:32.168 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--3
2016-02-25 17:08:32.168 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--4
2016-02-25 17:08:32.168 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--5
2016-02-25 17:08:32.169 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--6
2016-02-25 17:08:32.169 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--7
2016-02-25 17:08:32.169 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--8
2016-02-25 17:08:32.169 test[1959:391722] <NSThread: 0x7fbb98d24fa0>{number = 2, name = (null)}--9
結論:串行隊列,異步執行,開啓一條新的線程,按順序執行
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
代碼:
// 1. 建立併發隊列 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); // 2. 將任務添加到隊列, 而且指定同步執行 for (int i = 0; i < 10; i++) { dispatch_sync(queue, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }
輸出:
2016-02-25 17:18:38.039 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 0
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 1
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 2
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 3
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 4
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 5
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 6
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 7
2016-02-25 17:18:38.040 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 8
2016-02-25 17:18:38.041 test[1979:399667] <NSThread: 0x7ffef86024b0>{number = 1, name = main} 9
結論:併發隊列,同步執行,不開線程,順序執行
代碼:
// 1. 建立併發隊列 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); // 2. 將任務添加到隊列, 而且指定同步執行 for (int i = 0; i < 10; i++) { dispatch_async(queue, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }
輸出:
2016-02-25 17:22:59.357 test[1992:403694] <NSThread: 0x7fe531c1a9b0>{number = 7, name = (null)} 6
2016-02-25 17:22:59.356 test[1992:403684] <NSThread: 0x7fe531d18fa0>{number = 3, name = (null)} 1
2016-02-25 17:22:59.356 test[1992:403689] <NSThread: 0x7fe534300610>{number = 5, name = (null)} 3
2016-02-25 17:22:59.356 test[1992:403683] <NSThread: 0x7fe531e94d80>{number = 2, name = (null)} 0
2016-02-25 17:22:59.356 test[1992:403692] <NSThread: 0x7fe531e9df80>{number = 6, name = (null)} 4
2016-02-25 17:22:59.356 test[1992:403693] <NSThread: 0x7fe531d18f40>{number = 8, name = (null)} 5
2016-02-25 17:22:59.356 test[1992:403695] <NSThread: 0x7fe5343015e0>{number = 9, name = (null)} 7
2016-02-25 17:22:59.357 test[1992:403688] <NSThread: 0x7fe531c16e30>{number = 4, name = (null)} 2
2016-02-25 17:22:59.357 test[1992:403694] <NSThread: 0x7fe531c1a9b0>{number = 7, name = (null)} 9
2016-02-25 17:22:59.357 test[1992:403696] <NSThread: 0x7fe531c237a0>{number = 10, name = (null)} 8
結論:開啓足夠多的線程,不按照順序執行
CPU在調度的時候以最高效的方式調度和執行任務,因此會開啓多條線程,由於併發,執行順序不必定
先進先出的,只有當主線程的代碼執行完畢後,主隊列纔會調度任務到主線程執行
代碼
// 1. 獲取主隊列 dispatch_queue_t q = dispatch_get_main_queue(); // 2. 將任務添加到主隊列, 而且指定異步執行 for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); } // 先執行完這句代碼, 纔會執行主隊列中的任務 NSLog(@"hello %@", [NSThread currentThread]);
打印
2016-02-25 21:10:43.293 test[773:786816] hello <NSThread: 0x7ff158c05940>{number = 1, name = main}
2016-02-25 21:10:43.295 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 0
2016-02-25 21:10:43.295 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 1
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 2
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 3
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 4
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 5
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 6
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 7
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 8
2016-02-25 21:10:43.296 test[773:786816] <NSThread: 0x7ff158c05940>{number = 1, name = main} 9
打印結果得出的一些結論
代碼
NSLog(@"begin"); // 1. 獲取主隊列 dispatch_queue_t q = dispatch_get_main_queue(); // 2. 將任務添加到主隊列, 而且指定同步執行 // 死鎖 for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); } NSLog(@"end");
打印
2016-02-25 21:19:25.986 test[791:790967] begin
打印結果能夠看出,不是想要的結果,這時候發生了死鎖
在主線程執行,主隊列同步執行任務,會發生死鎖,主線程和主隊列同步任務相互等待,形成死鎖
代碼
NSLog(@"begin"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"--- %@", [NSThread currentThread]); // 1. 獲取主隊列 dispatch_queue_t q = dispatch_get_main_queue(); // 2. 將任務添加到主隊列, 而且指定同步執行 // 死鎖 for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); } }); NSLog(@"end");
打印
2016-02-25 21:23:23.205 test[803:794826] begin
2016-02-25 21:23:23.206 test[803:794826] end
2016-02-25 21:23:23.206 test[803:794866] --- <NSThread: 0x7f8830514cb0>{number = 2, name = (null)}
2016-02-25 21:23:23.209 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 0
2016-02-25 21:23:23.209 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 1
2016-02-25 21:23:23.209 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 2
2016-02-25 21:23:23.209 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 3
2016-02-25 21:23:23.209 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 4
2016-02-25 21:23:23.210 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 5
2016-02-25 21:23:23.210 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 6
2016-02-25 21:23:23.210 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 7
2016-02-25 21:23:23.210 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 8
2016-02-25 21:23:23.210 test[803:794826] <NSThread: 0x7f8830507dd0>{number = 1, name = main} 9
全局隊列是系統提供的,無需本身建立,能夠直接經過dispatch_get_global_queue(long identifier, unsigned long flags);函數來獲取。
代碼
// 1. 獲取全局隊列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // 2. 將任務添加到全局隊列, 異步執行 for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%d %@", i, [NSThread currentThread]); }); }
打印輸出
2016-02-25 21:29:06.978 test[816:799523] 1 <NSThread: 0x7fd428e15760>{number = 3, name = (null)}
2016-02-25 21:29:06.978 test[816:799530] 4 <NSThread: 0x7fd428d2fbb0>{number = 6, name = (null)}
2016-02-25 21:29:06.978 test[816:799522] 0 <NSThread: 0x7fd428f094e0>{number = 2, name = (null)}
2016-02-25 21:29:06.978 test[816:799529] 3 <NSThread: 0x7fd428c0e1b0>{number = 5, name = (null)}
2016-02-25 21:29:06.978 test[816:799532] 6 <NSThread: 0x7fd428f06740>{number = 7, name = (null)}
2016-02-25 21:29:06.978 test[816:799533] 7 <NSThread: 0x7fd428d37be0>{number = 8, name = (null)}
2016-02-25 21:29:06.978 test[816:799531] 5 <NSThread: 0x7fd428e0c490>{number = 9, name = (null)}
2016-02-25 21:29:06.978 test[816:799526] 2 <NSThread: 0x7fd428d3e4b0>{number = 4, name = (null)}
2016-02-25 21:29:06.979 test[816:799534] 8 <NSThread: 0x7fd428d36ab0>{number = 10, name = (null)}
2016-02-25 21:29:06.979 test[816:799523] 9 <NSThread: 0x7fd428e15760>{number = 3, name = (null)}
* 全局隊列沒有名稱,不管ARC仍是MRC都不須要考慮內存釋放,平常開發,建議使用全局隊列 * 併發隊列有名稱,若是在MRC開發中,須要使用dispatch_release來釋放相應的對象,dispatch_barrier 必須使用自定義的併發隊列,開發第三方框架,建議使用併發隊列
dispatch_get_global_queue(long identifier, unsigned long flags);
這個函數中有兩個參數:
第一個參數: identifier
iOS7.0,表示的是優先級:
DISPATCH_QUEUE_PRIORITY_HIGH = 2; 高優先級
DISPATCH_QUEUE_PRIORITY_DEFAULT = 0; 默認優先級
DISPATCH_QUEUE_PRIORITY_LOW = -2; 低優先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND = INT16_MIN; 後臺優先級
iOS8.0開始,推薦使用服務質量(QOS):
QOS_CLASS_USER_INTERACTIVE = 0x21; 用戶交互
QOS_CLASS_USER_INITIATED = 0x19; 用戶指望
QOS_CLASS_DEFAULT = 0x15; 默認
QOS_CLASS_UTILITY = 0x11; 實用工具
QOS_CLASS_BACKGROUND = 0x09; 後臺
QOS_CLASS_UNSPECIFIED = 0x00; 未指定
經過對比可知: 第一個參數傳入0,能夠同時適配iOS7及iOS7之後的版本。
服務質量和優先級是一一對應的:
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
第二個參數: flags
爲將來保留使用的,始終傳入0。
Reserved for future use.
對主隊列而言,無論是同步執行仍是異步執行,都不會開線程。
代碼
- (void)viewDidLoad { [super viewDidLoad]; //建立操做,而後調用操做的start方法 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo) object:nil]; NSLog(@"%d",op.isFinished); [op start]; NSLog(@"%d",op.isFinished); } - (void)demo { NSLog(@"hello %@",[NSThread currentThread]); }
打印輸出
2016-02-25 22:12:30.054 test[892:834660] 0
2016-02-25 22:12:30.054 test[892:834660] hello <NSThread: 0x7fad12704f80>{number = 1, name = main}
2016-02-25 22:12:30.054 test[892:834660] 1
結論:[op start]在主線程中調用的,因此執行的線程也會是在主線程執行! 重複調用start也只會執行一次,由於NSOperation會有一個屬性去記住,是否已經完成了該操做!
代碼
- (void)viewDidLoad { [super viewDidLoad]; // 建立操做,將操做添加到NSOperationQueue中,而後就會異步的自動執行 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo) object:nil]; //隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //把操做添加到隊列 [queue addOperation:op]; } - (void)demo { NSLog(@"hello %@",[NSThread currentThread]); }
打印
2016-02-25 22:21:44.999 test[912:842412] hello <NSThread: 0x7fab92610080>{number = 2, name = (null)}
將操做添加到NSOperationQueue中,而後就會異步的自動執行
NSBlockOperation 中使用block的方式讓全部代碼邏輯在一塊兒,使用起來更加簡便。
代碼
//建立操做 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"hello %@",[NSThread currentThread]); }]; //更新op的狀態,執行main方法,不會開新線程 [op start];
輸出
2016-02-25 22:25:30.442 test[923:846208] hello <NSThread: 0x7fd410d055a0>{number = 1, name = main}
代碼
// 建立隊列,建立操做,將操做添加到隊列中執行 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"hello %@",[NSThread currentThread]); }]; [queue addOperation:op];
輸出
2016-02-25 22:26:48.064 test[934:848038] hello <NSThread: 0x7fc6bbb24c80>{number = 2, name = (null)}
代碼
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock:^{ NSLog(@"hello %@",[NSThread currentThread]); }];
輸出
2016-02-25 22:27:56.445 test[945:850128] hello <NSThread: 0x7f98dbc2cae0>{number = 2, name = (null)}
建立隊列,添加block形式的操做
[self.queue addOperationWithBlock:^{ NSLog(@"異步下載圖片"); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"回到主線程,更新UI"); }]; }];
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"下載圖片---%@",[NSThread currentThread]); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"回到主線程刷新圖片的顯示 -%@",[NSThread currentThread]); }); });
這文章寫了很久,,過年一直到如今,終於寫完。。。
轉載請註明來自吃飯睡覺擼碼的博客 http://www.cnblogs.com/Erma-king/,幷包含相關連接。