線程
一個線程中任務的執行是串行的
同一時間內,一個線程只能執行一個任務
線程是進程中的一條執行路徑
多線程
一個進程中可以開啟多條線程,多條線程可以并行(同時)執行不同的任務
多線程原理
同一時間,CPU只能處理1條線程,只有一條線程在工作(執行)
多線程并發(同時)執行,其實是CPU快速在多條線程之間調度(切換)
多核CPU是真正意義上的多線程
CPU在多個線程之間調度,會消耗大量的CPU資源
每條線程被調度執行的頻次會降低(線程執行效率會降低)
優點:
能適當提高程序的執行效率
缺點:
創建線程是有開銷的,iOS下主要成本包括:內核數據結構(大約1KB)、棧空間(子線程512KB、主線程1MB)
也可以使用-setStackSize設置,但必須是4K的倍數,而且最小為16K,創建線程大約需要90毫秒的創建時間
開啟大量的線程,會降低程序的性能
線程越多,CPU在調度線程上的開銷越大
程序設計更加復雜:比如線程之間的通信、多線程的數據共享
多線程在iOS中的應用
一個iOS程序運行后,默認開啟一個線程,稱為主線程或UI線程
主線程的作用:
顯示、刷新UI界面
處理UI事件(點擊、滾動、拖拽事件等)
多線程使用注意:
別將比較耗時的操作放到主線程中,耗時操作會卡住主線程,嚴重影響UI流暢度
iOS多線程實現方案
- pthread C 通用的多線程API,跨平臺,程序員手動管理線程生命周期,使用難度大,iOS中幾乎不用
- NSThread OC 面向對象,簡單易用,只負責創建,不用管理線程死亡,偶爾使用
- GCD C 替代NSThread等線程技術,可以充分利用設備的多核,線程生命周期自動管理,經常使用
- NSOperation OC 基于GCD(底層是GCD),比GCD多了一些簡單實用的功能,更加面向對象,自動管理線程生命周期,經常使用。
pthread
需要引入#import <pthread.h>
//創建方法
int pthread_create(pthread_t * __restrict, const pthread_attr_t * __restrict,
void *(*)(void *), void * __restrict);
//使用
pthread_t thread;
pthread_create(&thread, NULL, func, NULL);
void * func(void *param)
{
for (NSInteger i = 0; i<50000; i++) {
NSLog(@"----%zd--%@", i, [NSThread currentThread]);
}
return NULL;
}
NSThread
創建、啟動線程
- 方法一
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(func) object:nil];
[thread start];
//線程啟動時,就會在線程thread中執行self中的func方法
- 方法二
//創建線程后自動啟動線程
[NSThread detachNewThreadSelector:(SEL) toTarget:(id) withObject:(id)];
- 方法三
//隱式創建并啟動線程
[self performSelectorInBackground:(SEL) withObject:(id)];
主線程相關用法:
+ (NSThread *)mainThread; //獲得主線程
+ (BOOL)isMainThread; //是否為主線程
- (BOOL)isMainThread; //是否為主線程
線程阻塞:
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
線程死亡:
- (void)exit; // 強制退出線程
GCD
會自動利用更多的CPU內核,會自動管理線程的生命周期,不需要寫管理線程的代碼。定制任務 將任務添加到隊列中 GCD會自動將隊列中的任務取出,放到線程中去執行,任務的取出遵循FIFO原則。
任務 (是否有開啟新線程的能力)
- 同步
在當前線程中執行
dispatch_sync(dispatch_queue_t queue, ^(void)block)
- 異步
可以在新的線程中執行,有開新線程的能力(不是一定會開新線程,比如放在主隊列中)
dispatch_async(dispatch_queue_t queue, ^(void)block)
隊列 (影響任務的執行方式)
- 并發 (允許開啟多個線程)
// 創建并發隊列
dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_CONCURRENT);
- 串行
// 創建串行隊列
dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_SERIAL);
// DISPATCH_QUEUE_SERIAL 可以傳NULL
獲取全局并發隊列
// 兩個參數 優先級 保留參數傳0
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
獲取全局串行主隊列(主隊列中的任務都在主線程中執行)
dispatch_queue_t queue = dispatch_get_main_queue();
不可以在主隊列中添加同步任務,因為同步任務立刻會執行,選成死鎖
| | 并發隊列 | 手動創建的串行隊列 | 主隊列|
| ------------- |:-------------:| -----:|
| 同步sync |沒有開啟新線程 串行執行任務|沒有開啟新線程 串行執行任務|沒有開啟新線程 串行執行任務|
| 異步async |有開啟新線程 并發執行任務|有開啟新線程 串行執行任務|沒有開啟新線程 串行執行任務|
GCD一些方法示例
/** 并發 異步 全局并發隊列 */
- (void)GCDTest1 {
/**
@param identifier 優先級
@param flags 保留參數 默認傳0
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"-------1-------");
});
dispatch_async(queue, ^{
NSLog(@"-------2-------");
});
dispatch_async(queue, ^{
NSLog(@"-------3-------");
});
}
/** 并發 異步 自定義并發隊列 */
- (void)GCDTest2 {
dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"-------1-------");
});
dispatch_async(queue, ^{
NSLog(@"-------2-------");
});
dispatch_async(queue, ^{
NSLog(@"-------3-------");
});
}
/** 并發 異步 自定義并發隊列 圍欄函數 */
- (void)GCDTest3 {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"-------1-------");
});
dispatch_async(queue, ^{
NSLog(@"-------2-------");
});
dispatch_barrier_async(queue, ^{
NSLog(@"-------圍欄函數-------");
});
dispatch_async(queue, ^{
NSLog(@"-------3-------");
});
}
/** 利用一次性函數寫單列 */
+ (instancetype)GCDTest4 {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_singleton = [[Singleton alloc] init];
});
return _singleton;
}
/** 延遲執行 */
- (void)GCDTest5 {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延時2秒打印");
});
}
/** GCD group */
- (void)GCDTest6 {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"-------1-------");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"-------2-------");
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"-------3-------");
});
}
多線程的安全隱患
資源共享:
一塊資源可能會被多個線程共享,比如多個線程訪問同一個對象、對一個變量、同一個文件
解決方法:
互斥鎖
@synchronized(鎖對象){ //需要鎖定的代碼 }
不推薦使用,消耗性能
鎖定1份代碼只能用1把鎖,用多把是無效的
線程同步技術
原子和非原子屬性
atomic 默認 原子屬性 為setter方法加鎖
線程安全,需要消耗大量的資源
nonatomic 非原子屬性 不會為setter方法加鎖
非線程安全,適合內存小的移動設備
建議:
所有屬性都聲明為nonatomic
盡量避免多線程搶奪同一塊資源
盡量加加鎖、資源搶奪的業務邏輯交給服務器處理,減小移動客戶端壓力
線程之間的通信
在一個進程中,線程往往不是孤立存在的,多個線程之間需要經常進行通信
如:
一個線程傳遞數據給另一個線程
在一個線程中執行完特定任務后,轉到另一個線程中繼續執行任務
常用方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
NSPort 端口對象 在兩個線程之間通信
NSMessagePort
NSMachPort