須要手動去管理線程的生命週期 可是他能夠方便咱們知道當前線程是什麼類的線程 可讓咱們知道當前線程的各類屬性 服務器
建立並啓動多線程
先建立 再啓動併發
// 建立 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; // 啓動 [thread start];
建立並自動啓動異步
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
除了建立啓動以外 NSThread還有不少方法 async
//取消線程 - (void)cancel; //啓動線程 - (void)start; //判斷某個線程的狀態的屬性 @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isCancelled) BOOL cancelled; //設置和獲取線程名字 -(void)setName:(NSString *)n; -(NSString *)name; //獲取當前線程信息 + (NSThread *)currentThread; //獲取主線程信息 + (NSThread *)mainThread; //使當前線程暫停一段時間,或者暫停到某個時刻 + (void)sleepForTimeInterval:(NSTimeInterval)time; + (void)sleepUntilDate:(NSDate *)date;
充分的利用了CPU 自動管理線程的生命週期 不須要咱們去完成 咱們只須要告訴他咱們想要作什麼就行spa
GCD中有兩個很是重要的概念 : 任務 和 隊列線程
任務: 就是說明你想要作什麼 說白了就是一段代碼 在GCD中就是一段blockcode
任務有兩種執行方式 同步執行 和異步執行orm
同步執行: 他會阻塞當前線程 等待Block中的任務執行完畢 而後當前線程纔會繼續往下運行.對象
異步執行:當前線程會直接往下執行,它不會阻塞當前線程
隊列:用於存聽任務。一共有兩種隊列 串行隊列和並行隊列
串行隊列: 放到串行隊列的任務 會取出來一個 執行一個 而後取下一個 這樣一個一個的執行。
並行隊列:取出來一個會放到別的線程,而後再取取出來再放到另外一個線程 這樣因爲取的動做很快 忽略不計 看起來全部的任務都是一塊兒執行的
同步串行 : 當前線程 一個一個執行
同步並行 : 當前線程 一個一個執行
異步串行 : 其餘線程 一個一個執行
異步並行 : 開不少線程 一塊兒執行
主隊列: 特殊的串行隊列 用於刷新UI 任何須要刷新UI的工做都要在祝隊列執行 因此通常耗時的任務都放在別的線程執行
本身建立的隊列 : 串行隊列和並行隊列 有兩個參數 第一個參數是一個標識符 第二個參數用來表示是串行的仍是並行的
//串行隊列 dispatch_queue_t queue = dispatch_queue_create("my Queue", NULL); dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_SERIAL); //並行隊列 dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_CONCURRENT);
全局並行隊列:只要是並行任務通常都加入到這個隊列 這是系統提供的一個併發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
同步任務 : 會阻塞當前線程
dispatch_sync(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]); });
異步任務:不會阻塞當前線程
dispatch_async(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]); });
爲了更好的理解 看兩個示例
如下代碼在主線程調用 會是什麼結果
NSLog(@"以前 - %@", [NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"sync - %@", [NSThread currentThread]); }); NSLog(@"以後 - %@", [NSThread currentThread]);
答案:
只會打印第一句: 以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main},而後主線程就卡死了,你能夠在界面上放一個按鈕,你就會發現點不了了。
解釋:
同步任務會阻塞當前線程,而後把 Block 中的任務放到指定的隊列中執行,只有等到 Block 中的任務完成後纔會讓當前線程繼續往下運行。
那麼這裏的步驟就是:打印完第一句後 dispath_sync 當即阻塞當前的主線程,而後把 Block 中的任務放到 main_queue 中,但是 main_queue 中的任務會被取出來放到主線程中執行,但主線程這個時候已經被阻塞了,因此 Block 中的任務就不能完成,它不完成 dispath_sync 就會一直阻塞主線程,這就是死鎖現象。致使主線程一直卡死。
以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main}
如下代碼會產生什麼結果
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"以前 - %@", [NSThread currentThread]); dispatch_async(queue, ^{ NSLog(@"sync以前 - %@", [NSThread currentThread]); dispatch_sync(queue, ^{ NSLog(@"sync - %@", [NSThread currentThread]); }); NSLog(@"sync以後 - %@", [NSThread currentThread]); }); NSLog(@"以後 - %@", [NSThread currentThread]);
答案:
2016-12-26 13:25:31.689 ppp[41807:1430208] 以前 - <NSThread: 0x60800006ccc0>{number = 1, name = main}
2016-12-26 13:25:31.690 ppp[41807:1430208] 以後 - <NSThread: 0x60800006ccc0>{number = 1, name = main}
2016-12-26 13:25:31.690 ppp[41807:1430277] sync以前 - <NSThread: 0x608000261280>{number = 4, name = (null)}
很明顯 sync - %@ 和sync以後 - %@ 沒有被打印出來 崩潰了
分析:
咱們按執行順序一步一步來:
1.使用 DISPATCH_QUEUE_SERIAL 這個參數,建立了一個串行隊列
2.打印出 以前 - %@ 這句
3.dispatch_async 異步執行 因此當前線程不會被阻塞 就有了兩條線程 一條當前線程繼續往下打印出 以後 - %@ 這句 另外一條執行Block中的內容 打印 sync以前 - %@這句 由於這兩條是並行的 因此打印前後順序無所謂
4.如今的狀況和上一個例子同樣了 dispath_sync 同步執行 因而它所在的線程會被阻塞 一直等到 sync 裏的任務執行完纔會繼續往下 因而 sync 就高興的把本身 Block 中的任務放到 queue 中 但是 queue 是一個串行隊列 一次執行一個任務 因此 sync 的 Block 必須等到前一個任務執行完畢 可沒想到的是 queue 正在執行的任務就是被 sync 阻塞了的那個 因而又發生了死鎖 因此 sync 所在的線程被卡死了 剩下的兩句代碼天然不會打印
NSOperation 是對GCD的封裝 徹底面向對象 NSOperation和NSOperationQueue分別對應GCD的任務和隊列.
1.將要執行的任務封裝到一個NSOperation對象中
2.將此任務添加到一個NSOperationQueue對象中
系統會自動執行任務
NSOperation是一個抽象類 不能封裝任務 能夠用他的兩個子類用於封裝任務 分別是: NSInvocationOperation和NSBlockOperation 建立一個Operation後 須要用start方法來調用 它會默認在當前隊列同步執行 也能夠在中途取消一個任務 調用cancel方法.
NSInvocationOperation : 須要傳入一個方法名。
//1.建立NSInvocationOperation對象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2.開始執行 [operation start];
NSBlockOperation
//1.建立NSBlockOperation對象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //2.開始任務 [operation start];
以前說過這樣的任務,默認會在當前線程執行。可是NSBlockOperation還有一個方法: addExecutionBlock:,經過這個方法能夠給 Operation 添加多個執行 Block。這樣 Operation 中的任務 會併發執行,它會 在主線程和其它的多個線程 執行這些任務,注意下面的打印結果:
//1.建立NSBlockOperation對象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //添加多個Block for (NSInteger i = 0; i < 5; i++) { [operation addExecutionBlock:^{ NSLog(@"第%ld次:%@", i, [NSThread currentThread]); }]; } //2.開始任務 [operation start];
打印輸出
2016-12-26 13:43:04.015 ppp[42039:1438406] <NSThread: 0x60800006f540>{number = 1, name = main}
2016-12-26 13:43:04.015 ppp[42039:1438524] 第2次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}
2016-12-26 13:43:04.015 ppp[42039:1438522] 第0次:<NSThread: 0x6000002622c0>{number = 3, name = (null)}
2016-12-26 13:43:04.015 ppp[42039:1438521] 第1次:<NSThread: 0x60000026ba80>{number = 4, name = (null)}
2016-12-26 13:43:04.016 ppp[42039:1438406] 第3次:<NSThread: 0x60800006f540>{number = 1, name = main}
2016-12-26 13:43:04.016 ppp[42039:1438524] 第4次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}
NOTE: addExecutionBlock:方法必須在 start() 方法以前執行,不然就會報錯:
‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
主隊列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
其餘隊列
//1.建立一個其餘隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2.建立NSBlockOperation對象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //3.添加多個Block for (NSInteger i = 0; i < 5; i++) { [operation addExecutionBlock:^{ NSLog(@"第%ld次:%@", i, [NSThread currentThread]); }]; } //4.隊列添加任務 [queue addOperation:operation];
打印輸出
2016-12-26 13:48:55.611 ppp[42100:1441056] 第1次:<NSThread: 0x608000462240>{number = 6, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441055] <NSThread: 0x600000261800>{number = 3, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441067] 第2次:<NSThread: 0x60800027a740>{number = 4, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441058] 第0次:<NSThread: 0x608000462180>{number = 5, name = (null)}
2016-12-26 13:48:55.612 ppp[42100:1441056] 第3次:<NSThread: 0x608000462240>{number = 6, name = (null)}
2016-12-26 13:48:55.612 ppp[42100:1441055] 第4次:<NSThread: 0x600000261800>{number = 3, name = (null)}
沒有串行隊列?
NSOperationQueue有一個參數 maxConcurrentOperationCount 最大併發數,用來設置最多可讓多少個任務同時執行。當你把它設置爲 1
的時候,他就是串行!
NSOperationQueue還有一個添加任務的方法- (void)addOperationWithBlock:(void (^)(void))block; 這樣能夠添加一個任務到隊列中 十分方便
NSOperation有一個很是實用的功能 那就是添加依賴 好比有 3 個任務 A: 從服務器上下載一張圖片 B:給這張圖片加個水印 C:把圖片返回給服務器。這時就能夠用到依賴了.
//1.任務一:下載圖片 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下載圖片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //2.任務二:打水印 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"打水印 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //3.任務三:上傳圖片 NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"上傳圖片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //4.設置依賴 [operation2 addDependency:operation1]; //任務二依賴任務一 [operation3 addDependency:operation2]; //任務三依賴任務二 //5.建立隊列並加入任務 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
打印結果
2016-12-26 13:54:33.953 ppp[42218:1444656] 下載圖片 - <NSThread: 0x60800007ac00>{number = 3, name = (null)}
2016-12-26 13:54:35.028 ppp[42218:1444659] 打水印 - <NSThread: 0x60800007f280>{number = 4, name = (null)}
2016-12-26 13:54:36.103 ppp[42218:1444659] 上傳圖片 - <NSThread: 0x60800007f280>{number = 4, name = (null)}
注意: 不能添加相互依賴 會死鎖 好比A依賴B B依賴A
可使用 removeDependency 來解除依賴關係
能夠在不一樣的隊列之間依賴 反正就是這個依賴是添加到任務身上的 和隊列不要緊
還有一些方法
NSOperation
BOOL executing; //判斷任務是否正在執行
BOOL finished; //判斷任務是否完成
void (^completionBlock)(void); //用來設置完成後須要執行的操做
- (void)cancel; //取消任務
- (void)waitUntilFinished; //阻塞當前線程直到此任務執行完畢
NSOperationQueue
NSUInteger operationCount; //獲取隊列的任務數
- (void)cancelAllOperations; //取消隊列中全部的任務
- (void)waitUntilAllOperationsAreFinished; //阻塞當前線程直到此隊列中的全部任務執行完畢
[queue setSuspended:YES]; // 暫停queue
[queue setSuspended:NO]; // 繼續queue
NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
GCD
dispatch_async(dispatch_get_main_queue(), ^{ });
NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ }];
好啦 就這麼多啦 可能寫的也不是特別的好 多線程也不止這麼些東西 謝謝你們