多线程实现的几种方式
多線程實現一共有四種方式,如下圖:
- pthread的使用
- 定義pthread
typedef __darwin_pthread_t pthread_t;- 創建pthread
int pthread_create(pthread_t * __restrict, const pthread_attr_t * __restrict,void *(*)(void *), void * __restrict);- 范例
void * run(void *param) {for (NSInteger i = 0; i<50000; i++) {NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);}return NULL; }- (IBAction)buttonClick:(id)sender {pthread_t thread;pthread_create(&thread, NULL, run, NULL);pthread_t thread2;pthread_create(&thread2, NULL, run, NULL); }-?NSThread
-?創建和啟動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start];- 主線程相關用法
+ (NSThread *)mainThread; // 獲得主線程 - (BOOL)isMainThread; // 是否為主線程 + (BOOL)isMainThread; // 是否為主線程- 獲取當前線程
NSThread *current = [NSThread currentThread];- 線程的名字
- (void)setName:(NSString *)n; - (NSString *)name;- 其它方式創建線程
-?創建線程后自動啟動線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];-?隱式創建并啟動線程
[self performSelectorInBackground:@selector(run) withObject:nil];
-?上述2種創建線程方式的優缺點
-?優點:簡單快捷
-?缺點:無法對線程進行更詳細的設置
- 線程的狀態
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start];
-?阻塞(暫停)線程
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 進入阻塞狀態- 強制停止線程
+ (void)exit; // 進入死亡狀態注意:一旦線程停止(死亡)了,就不能再次開啟任務
?? - 多線程的隱患
-?資源共享
-?1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源
-?比如多個線程訪問同一個對象、同一個變量、同一個文件
? -?當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題
- 解決方法:互斥鎖
-?互斥鎖使用格式
@synchronized(鎖對象) { // 需要鎖定的代碼 }注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
-?互斥鎖的優缺點
- 優點:能有效防止因多線程搶奪資源造成的數據安全問題
-?缺點:需要消耗大量的CPU資源
-?互斥鎖的使用前提:多條線程搶奪同一塊資源
-?相關專業術語:線程同步
-?線程同步的意思是:多條線程在同一條線上執行(按順序地執行任務)
-?互斥鎖,就是使用了線程同步技術
- 原子和非原子屬性
-?OC在定義屬性時有nonatomic和atomic兩種選擇
-?atomic:原子屬性,為setter方法加鎖(默認就是atomic)
-?nonatomic:非原子屬性,不會為setter方法加鎖
-?nonatomic和atomic對比
-?atomic:線程安全,需要消耗大量的資源
-?nonatomic:非線程安全,適合內存小的移動設備
-?iOS開發的建議
-?所有屬性都聲明為nonatomic
-?盡量避免多線程搶奪同一塊資源
-?盡量將加鎖、資源搶奪的業務邏輯交給服務器端處理,減小移動客戶端的壓力
-?GCD的使用
-?什么是GCD
-?全稱是Grand Central Dispatch,可譯為“牛逼的中樞調度器”
-?純C語言,提供了非常多強大的函數
-?GCD的優勢
-?GCD是蘋果公司為多核的并行運算提出的解決方案
-?GCD會自動利用更多的CPU內核(比如雙核、四核)
-?GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
-?程序員只需要告訴GCD想要執行什么任務,不需要編寫任何線程管理代碼
-?GCD中有2個核心概念
-?任務:執行什么操作
-?隊列:用來存放任務
-?GCD的使用就2個步驟
-?定制任務
-?確定想做的事情
?
-?將任務添加到隊列中
-?GCD會自動將隊列中的任務取出,放到對應的線程中執行
-?任務的取出遵循隊列的FIFO原則:先進先出,后進后出
-?GCD中有2個用來執行任務的常用函數
-?用同步的方式執行任務
// queue:隊列 block:任務 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);-?用異步的方式執行任務
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);-?同步和異步的區別
-?同步:只能在當前線程中執行任務,不具備開啟新線程的能力
-?異步:可以在新的線程中執行任務,具備開啟新線程的能力
-?GCD中還有個用來執行任務的函數,在前面的任務執行結束后它才執行,而且它后面的任務等它執行完成之后才會執行:
// 這個queue不能是全局的并發隊列 dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);-?隊列的類型
-?GCD的隊列可以分為2大類型
-?并發隊列(Concurrent Dispatch Queue)
-?可以讓多個任務并發(同時)執行(自動開啟多個線程同時執行任務)
-?并發功能只有在異步(dispatch_async)函數下才有效
-?串行隊列(Serial Dispatch Queue)
-?讓任務一個接著一個地執行(一個任務執行完畢后,再執行下一個任務)
-?并發隊列
- 自己創建的
- 全局
-?串行隊列
- 主隊列
- 自己創建的
-?同步和異步主要影響:能不能開啟新的線程
-?同步:只是在當前線程中執行任務,不具備開啟新線程的能力
-?異步:可以在新的線程中執行任務,具備開啟新線程的能力
-?并發和串行主要影響:任務的執行方式
-?并發:允許多個任務并發(同時)執行
-?串行:一個任務執行完畢后,再執行下一個任務
?
-?并發隊列
// 使用dispatch_queue_create函數創建隊列 dispatch_queue_t dispatch_queue_create(const char *label, // 隊列名稱 dispatch_queue_attr_t attr); // 隊列的類型// 創建并發隊列 dispatch_queue_t queue = dispatch_queue_create("com.samyang.queue", DISPATCH_QUEUE_CONCURRENT); // GCD默認已經提供了全局的并發隊列,供整個應用使用,可以無需手動創建 使用dispatch_get_global_queue函數獲得全局的并發隊列 dispatch_queue_t dispatch_get_global_queue( dispatch_queue_priority_t priority, // 隊列的優先級 unsigned long flags); // 此參數暫時無用,用0即可// 獲得全局并發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 全局并發隊列的優先級 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中) #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺-?串行隊列
// GCD中獲得串行有2種途徑 // 使用dispatch_queue_create函數創建串行隊列 // 創建串行隊列(隊列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL) dispatch_queue_t queue = dispatch_queue_create("com.samyang.queue", NULL); /*使用主隊列(跟主線程相關聯的隊列) 主隊列是GCD自帶的一種特殊的串行隊列 放在主隊列中的任務,都會放到主線程中執行 使用dispatch_get_main_queue()獲得主隊列*/ dispatch_queue_t queue = dispatch_get_main_queue();-?各種隊列的執行效果
-?注意:使用sync函數往當前串行隊列中添加任務,會卡住當前的串行隊列
- 延時執行
-?iOS常見的延時執行
// 調用NSObject的方法 [self performSelector:@selector(run) withObject:nil afterDelay:2.0]; // 2秒后再調用self的run方法// 使用GCD函數 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2秒后執行這里的代碼... });// 使用NSTimer [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];- 一次性代碼(比如說單例模式singleton)
// 使用dispatch_once函數能保證某段代碼在程序運行過程中只被執行1次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{// 只執行1次的代碼(這里面默認是線程安全的) });- 快速迭代
// 使用dispatch_apply函數能進行快速迭代遍歷 dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){// 執行10次代碼,index順序不確定 });- 隊列組
-有這么1種需求
-?首先:分別異步執行2個耗時的操作
-?其次:等2個異步操作都執行完畢后,再回到主線程執行操作
// 如果想要快速高效地實現上述需求,可以考慮用隊列組 dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 執行1個耗時的異步操作 }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 執行1個耗時的異步操作 }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的異步操作都執行完畢后,回到主線程... });-?NSOperation
NSOperationQueue的隊列類型
- 主隊列
- [NSOperationQueue mainQueue]
- 凡是添加到主隊列中的任務(NSOperation),都會放到主線程中執行
- 非主隊列(其他隊列)
- [[NSOperationQueue alloc] init]
- 同時包含了:串行、并發功能
- 添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執行
NSOperation的作用
-
配合使用NSOperation和NSOperationQueue也能實現多線程編程
-
NSOperation和NSOperationQueue實現多線程的具體步驟
- 先將需要執行的操作封裝到一個NSOperation對象中
- 然后將NSOperation對象添加到NSOperationQueue中
- 系統會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新線程中執行
NSOperation的子類
-
NSOperation是個抽象類,并不具備封裝操作的能力,必須使用它的子類
-
使用NSOperation子類的方式有3種
- NSInvocationOperation
- NSBlockOperation
- 自定義子類繼承NSOperation,實現內部相應的方法
NSInvocationOperation
- 創建NSInvocationOperation對象
- 調用start方法開始執行操作
-
一旦執行操作,就會調用target的sel方法
-
注意
- 默認情況下,調用了start方法后并不會開一條新線程去執行操作,而是在當前線程同步執行操作
- 只有將NSOperation放到一個NSOperationQueue中,才會異步執行操作
-
NSBlockOperation
- 創建NSBlockOperation對象
- 注意:只要NSBlockOperation封裝的操作數 > 1,就會異步執行
-?NSOperationQueue
-?NSOperationQueue的作用
- NSOperation可以調用start方法來執行任務,但默認是同步執行的
-?如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統會自動異步執行NSOperation中的操作
-?添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;- (void)addOperationWithBlock:(void (^)(void))block;- 最大并發數
-?什么是并發數
-?同時執行的任務數
-?比如,同時開3個線程執行3個任務,并發數就是3
-?最大并發數的相關方法
- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
-?隊列的取消、暫停、恢復
?取消隊列的所有操作
- (void)cancelAllOperations;-?提示:也可以調用NSOperation的- (void)cancel方法取消單個操作
?-?暫停和恢復隊列
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢復隊列- (BOOL)isSuspended;-?操作優先級
設置NSOperation在queue中的優先級,可以改變操作的執行優先級
- (NSOperationQueuePriority)queuePriority;- (void)setQueuePriority:(NSOperationQueuePriority)p;-?優先級的取值
NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8-?操作依賴
-?NSOperation之間可以設置依賴來保證執行順序
- 比如一定要讓操作A執行完后,才能執行操作B,可以這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作A-?可以在不同queue的NSOperation之間創建依賴關系
注意:不能相互依賴,比如A依賴B,B依賴A
-?操作的監聽
-?可以監聽一個操作的執行完畢
- (void (^)(void))completionBlock;- (void)setCompletionBlock:(void (^)(void))block;
-?自定義NSOperation
-?自定義NSOperation的步驟很簡單
-?重寫- (void)main方法,在里面實現想執行的任務
-?重寫- (void)main方法的注意點
-?自己創建自動釋放池(因為如果是異步操作,無法訪問主線程的自動釋放池)
-?經常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應
多線程實現的幾種方式 - samyang1107 - 博客園 (cnblogs.com)
總結
以上是生活随笔為你收集整理的多线程实现的几种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Objective-C基础——面对对象语
- 下一篇: 关于微信H5页面开发中音乐不自动播放的解