1.多線程概念
- 1.什么是多線程
事實上,同一時間內單核的CPU只能執(zhí)行一個線程,多線程是CPU快速的在多個線程之間進行切換(調度),造成了多個線程同時執(zhí)行的假象。
如果是多核CPU就真的可以同時處理多個線程了。 - 2.為什么要用多線程
多線程的目的是為了同步完成多項任務,通過提高系統(tǒng)的資源利用率來提高系統(tǒng)的效率 - 3.多線程的優(yōu)缺點
1.優(yōu)點
能適當提高程序的運行效率
能適當提高資源利用率(CPU、內存利用率)
2.缺點
開啟線程需要占用一定的內存空間(默認情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內存空間,降低程序的性能
線程越多,CPU在調度線程上的開銷就越大
程序設計更加復雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
2.多線程的實現(xiàn)方式
(1)pthread(POSIX Threads):一套C語言編寫的跨平臺多線程API,使用難度大,需要手動管理線程生命周期。
(2)NSThread:面向對象操作線程,使用相對簡單,需要手動管理線程生命周期。
(3)GCD:蘋果多核編程解決方案,使用起來非常方便。需要自己實現(xiàn)如:限制并發(fā)數(shù),任務間的依賴等功能。自動管理線程生命周期。
(4)NSOperation:基于GCD的封裝,面向對象操作線程,提供了比GCD更豐富的API:限制最大并發(fā)數(shù),設置任務依賴關系。但是它不能直接使用,因為它是一個抽象類,可以繼承它或者使用系統(tǒng)定義NSInvocationOperation或NSBlockOperation。自動管理線程生命周期。
3.GCD
任務:任務就是執(zhí)行操作,指的是block中的代碼
同步執(zhí)行(sync):是指不具備開辟新線程的能力
異步執(zhí)行(async):不會阻塞主線程,會開啟新線程執(zhí)行任務,在后臺執(zhí)行隊列:
這里的隊列就是任務隊列,即用來存放任務的隊列。隊列是一種特殊的線性表,采用先進先出(FIFO)的原則,
每次新任務都會被插入到隊列尾部,而執(zhí)行隊列中的任務時,會從隊列頭部開始讀取并執(zhí)行。GCD中有兩種隊列:串行隊列和并行隊列
1.并行隊列(DISPATCH_QUEUE_CONCURRENT):可以多個任務同時進行,也就會開啟多個線程執(zhí)行任務。交替執(zhí)行。
2.串行隊列(DISPATCH_QUEUE_SERIAL):任務一個接著一個執(zhí)行,也就是一個任務執(zhí)行完后,下一個任務就開始。一個接著一個執(zhí)行。特殊隊列
1.全局隊列 dispatch_get_global_queue ,全局隊列就是并行隊列,供整個應用使用;需要兩個參數(shù),第一個是隊列優(yōu)先級(DISPATCH_QUEUE_PRIORITY_DEFAULT),第二個0即可(官方文檔說:For future use)
2.主隊列 dispatch_get_main_queue ,主隊列就是串行隊列,在應用啟動時,就創(chuàng)建好了,所以我們要用的時候就直接拿來用而不需要創(chuàng)建任務隊列組合情況
1.異步 + 串行 可以開辟新線程,但是任務只能一個一個取,所以沒必要開辟新線程 結果:單線程
2.異步 + 并行 可以開辟多線程,前一個任務一旦執(zhí)行,就可以調度下一個任務 結果 :多線程
3.同步 + 串行 即不可以開辟新線程,也不可以不等待前一任務完成就調度,完全沒必要開辟新線程 結果: 單線程
4.同步 + 并行 不可以開辟新線程,但是可以前一個任務一旦執(zhí)行,就可以調度下一個任務 結果:單線程
5.同步+主隊列 死鎖
6.異步+主隊列 只在主線程中執(zhí)行任務,執(zhí)行完一個任務,再執(zhí)行下一個任務-
三種死鎖情況(隊列阻塞)
1.主線程主隊列嵌套同步隊列
2.同步串行嵌套同步串行任務
3.異步串行嵌套同步串行任務3.1GCD常用方法
- 1.柵欄函數(shù)
dispatch_barrier_sync(myconcurrent, ^{ }); 作用相同,但是這個會堵塞線程,影響后面的執(zhí)行
dispatch_barrier_async(myconcurrent, ^{ }); 前面的任務執(zhí)行完畢,才會來到這里,不影響后面任務的執(zhí)行
- 1.柵欄函數(shù)
dispatch_queue_t queue = dispatch_queue_create("testqueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
NSLog(@"center");
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
NSLog(@"end");
//結果 12結束后再執(zhí)行34
//dispatch_barrier_async和dispatch_barrier_sync區(qū)別 (都是在柵欄函數(shù)之前的先執(zhí)行,在柵欄后面的后執(zhí)行)
//dispatch_barrier_async不會阻塞當前線程block添加到queue中后就會立即返回執(zhí)行線程中后面的方法
//dispatch_barrier_sync會阻塞當前線程
- 2.GCD延時方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ }); - 3.只執(zhí)行一次操作(單例創(chuàng)建)
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{}); - 4.快速迭代方法 (同時遍歷)
dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
dispatch_apply(6, global_queue, ^(size_t index) {
NSLog(@"%zd %@", index, [NSThread currentThread]);
}); - 5.隊列組dispatch_group_t
第一種 會阻塞主線程,等待上面的任務執(zhí)行完,再繼續(xù)向下執(zhí)行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
第二種 不會阻塞主線程,等待上面的任務執(zhí)行完,該block就會執(zhí)行 (推薦)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"回到主線程 %@", [NSThread currentThread]); }); - 6.信號量dispatch_semaphore_t
dispatch_semaphore_wait當信號量的值大于0時,信號量會減1,當信號量的值等于0時,阻塞線程直到該信號量的值大于0或者達到等待時間
dispatch_semaphore_signal釋放信號量,使得該信號量的值加1
作用
實現(xiàn)異步任務同步執(zhí)行
限制最大并發(fā)數(shù)
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
sleep(3);
dispatch_semaphore_signal(sem);
NSLog(@"A --thread:%@", [NSThread currentThread]);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(3);
dispatch_semaphore_signal(sem);
NSLog(@"B --thread:%@", [NSThread currentThread]);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(3);
NSLog(@"C --thread:%@", [NSThread currentThread]);
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
4.NSOperation
- 1.什么是NSOperation
1.NSOperation是Apple提供給開發(fā)者的一套多線程解決方案,實際上是基于GCD的一套更高級封裝,完全Objective-C代碼。簡單、易用、代碼可讀性高。
2.NSOperation需要配合NSOperationQueue來實現(xiàn)多線程,因為默認情況下NSOperation單獨使用時是系統(tǒng)同步執(zhí)行操作,并沒有開啟新線程的能力,只有配合NSOperationQueue才能實現(xiàn)異步執(zhí)行
3.因為NSOperation是基于GCD的,那么使用起來也和GCD差不多,其中,NSOperation相當于GCD中的任務,而NSOperationQueue則相當于GCD中的隊列。 - 2.NSOperation實現(xiàn)多線程的使用步驟分為三步:
1.創(chuàng)建任務 NSBlockOperation/NSInvocationOperation
2.創(chuàng)建隊列 NSOperationQueue
主隊列NSOperationQueue *queue = [NSOperationQueue mainQueue];
其它隊列NSOperationQueue *queue = [[NSOperationQueue alloc] init];根據(jù)maxConcurrentOperationCount是否串行 1串行 >1并發(fā)默認并發(fā) - 3.操作依賴
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 3.添加依賴
[op2 addDependency:op1]; // 讓op2 依賴于 op1,則先執(zhí)行op1,在執(zhí)行op2
// 4.添加操作到隊列中
[queue addOperation:op1];
[queue addOperation:op2];