知識點
1.理解線程的概念
2.NSThread的使用
3.NSOperation的使用
4.GCD的使用
5.線程鎖,線程安全
===============================
1.多線程是一種實現多任務並發執行的技術,允許同時執行多個任務,能夠更合理的利用CPU的資源,提高效率、防止用戶界面卡頓。
在iOS中,所有的UI處理只能在主線程做。
什麼是進程?
· 簡單的說進程就是我們電腦上運行的一個個應用程序,每一個程序就是一個進程,並且每個進程之間是獨立的,每個進程運行在其專用受保護的內存空間內(window系統可以通過任務管理器進行查看,Mac系統中可以通過活動監視器對其進行查看)
什麼是線程?
· 通過上面的介紹我們知道了什麼是進程,那麼如何讓進程運行起來,這個時候就要有線程了,也就是說每個應用程序想要跑起來,最少也要有一條線程存在,其實應用程序啟動的時候我們的系統就會默認幫我們的應用程序開啟一條線程,這條線程也叫做’主線程’,或者’UI線程’
進程和線程之間的關系
· 線程是進行的執行單元,進程的所有任務都在線程中執行,舉例來說:進程就好比公司中的一個個部門,線程則代表著部門中的同事,而主線程當然是我們的老板了,一個公司部能沒有老板,一個程序不能沒有線程其實都是一個道理.
什麼是CPU?
· CPU(中央處理器,Central Processing Unit)是一塊超大規模的集成電路,只要用來解釋計算機指令以及處理計算機軟件中的數據.
多線程的原理
· 同一時間,CPU值能處理一個線程,只有一條線程在執行,多線程指的就是多條線程同時執行,其實就是CPU快速的在多條線程之間的切換,如果CPU調度線程的時間足夠快,那麼就會造成多線程並發執行的假象,而當線程特別多的時候,那麼CPU在多條切換的效率也就會下降,同時消耗大量的CPU資源,線程的執行效率也就會下降.
多線程優點
· 能適當提高程序的執行效率
· 能適當提高資源的利用率
多線程的缺點
· 開啟線程需要占用一定的內存空間,如果開啟大量的線程,則會占用大量的內存空間,降低程序的性能
· 線程越多,CPU在調度線程上得開銷就越大,程序的設計上也就更加的復雜,因此不推薦開啟太多的線程,一般開啟2~5條為最佳(且用且珍惜);
主線程
· 也就是應用程序啟動的時候,系統默認幫我們創建的線程,稱之為’主線程’或者是’UI線程’;
· 主線程的作用一般都是用來顯示或者刷新UI界面例如:點擊,滾動,拖拽等事件
===============================
2.NSThread線程控制
1).創建線程,並自動執行
[NSThread detachNewThreadSelector:@selector(doSomeThing) toTarget:self withObject:nil];
2).創建線程,不自動執行
[[NSThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
3).設置線程名
thread.name = @"另一個線程";
4).執行線程
[thread start];
5).線程取消
[thread cancel];
6).函數內獲取當前線程
[NSThread currentThread];
7).獲取主線程
[NSThread mainThread];
7).線程休眠
[NSThread sleepForTimeInterval:1.0f]; // 休眠幾秒
[NSThread sleepUntilDate:date]; // 休眠到指定時間
8).線程退出
[NSThread exit];
9).線程通信
[self performSelector:@selector(function) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];
===============================
3.NSOperation,是以任務為中心的一種多線程技術,並不直接管理線程
1).NSOperation是抽象父類,不要直接使用,而應該使用它的子類
NSInvocationOperation
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
NSBlockOperation
[NSBlockOperation blockOperationWithBlock:^{}];
添加任務間的依賴關系,前者依賴於後者的完成,也就是後者執行完,前者才能執行,依賴關系需要放在添加到隊列之前設置
[invocation addDependency:blockOperation];
如果有必要,可以讓Operation取消
[invocation cancel];
2.NSOperationQueue,任務隊列,NSOperation對象需要添加到隊列裡面才會執行
添加到隊列裡之後,自動會給每個NSOperation對象創建一個線程去執行
創建NSOperationQueue
[[NSOperationQueue alloc] init];
設置最大並發數
queue.maxConcurrentOperationCount = 1;
添加任務到隊列裡
[queue addOperation:blockOperation];
讓隊列裡面,所有的Operation都取消
[queue cancelAllOperations];
獲取當前線程對列
currentQueue
獲取主線程對列
mainQueue
===============================
4.GCD是一套C語言的線程控制API,是NSOperation的底層實現,用Block來表示一個任務
1).創建隊列
dispatch_queue_create(“QF Queue”, DISPATCH_QUEUE_CONCURRENT);
2).系統的隊列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 全局隊列
dispatch_get_main_queue(); // 主線程隊列
3).異步執行任務
dispatch_async(globalQuque, ^{});
4).創建分組
dispatch_group_create();
5).添加任務到分組,並執行
dispatch_group_async(group, globalQuque, ^{});
6).分組執行完的通知
dispatch_group_notify(group, mainQuque, ^{});
===============================
5.線程鎖
1).方式1:
_lock = [[NSLock alloc] init];
[_lock tryLock];
[_lock unlock];
2).方式2:
@synchronized(self) {}
NSThread
//多線程 -- 為了將一些耗時的操作放在多線程中去執行 主要是為了給一個好的用戶體驗,防止卡頓的效果
//越多越好? 消耗內存和cpu的使用效率
//創建多線程的方式
//1.NSThread
//2.CGD
//3.NSOperation
//程序運行的時候,會默認創建一個線程,這個線程稱之為主線程/UI線程,所有的UI相關的操作都需要在這個線程中執行
BOOL res1 = [NSThread isMainThread]; //判斷是否是子線程
BOOL RES2 = [NSThread isMultiThreaded]; //判斷是否是多線程
//1.創建線程 減方法創建
//操作 – 功能 – 若干行代碼 – 函數 – 任務
NSThread * thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
//第三個參數: object 如果第二個參數selector帶參的話 就使用object傳遞
//新創建的線程稱之為子線程或者任務線程/後台線程
//線程的名字
thread1.name = @"子線程1";
//線程的優先級(double) 0 - 1
thread1.threadPriority = 0.5;
//設置線程的優先級(枚舉值)需要在線程開啟前設置 否則無效。
thread1.qualityOfService = NSQualityOfServiceBackground;
//2.開啟線程
[thread1 start];
//創建線程 加方法創建並且執行線程
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];
//設置不了線程的名字 可以到方法裡通過currentThread方法拿到線程 再進行賦值操作
//NSObject方法 創建線程
[self performSelectorInBackground:@selector(threadRun) withObject:nil];
GCD
//GCD - 基於C語言的一套API接口,它是把Block作為任務,添加到隊列中進行操作
//GCD 優點:可以控制多個線程的執行順序 這個功能是NSThread不具有的
//缺點: 不能取消線程
//GCD 語法都是c語言函數
//1.創建隊列
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
//queue 隊列/線程池
//dispatch 調度/執行
//第一個參數 隊列的名字 通常寫nil
//第二個參數 串行/並行
//DISPATCH_QUEUE_SERIAL 串行 按順序執行隊列裡的線程
//DISPATCH_QUEUE_CONCURRENT 並行 同時執行若干個線程
監聽線程的退出
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadOut) name:NSThreadWillExitNotification object:nil];
//NSThreadWillExitNotification 線程退出的頻道
//只需要監聽這個頻道 當線程退出的時候 就會收到通知/消息 然後調用響應的方法
//取消線程 cancel並不能把一個線程退出 調用cancle方法只是為了告訴當前線程的isCancelled屬性為YES 並且觸發監聽
[thread cancel];
if (thread.isCancelled) {
//如果為YES 退出線程 也可以使用return
[NSThread exit];
}
//threadRun這個方法是我們子線程的入口函數 - 當這個方法執行完畢的時候,線程就已經是死亡狀態了
定時器
//GCD定時器
//1.創建定時器
//需要把定時器定義成全局變量 否則會被立刻釋放
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//2.設置定時器 第一個參數為時間間隔 第二個參數次數
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.定時器定時的操作
dispatch_source_set_event_handler(self.timer, ^{
//定時器每隔幾秒鐘要做的操作
NSLog(@"d");
});
//執行定時器
dispatch_resume(self.timer);
延時操作
[NSThread sleepForTimeInterval:2.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
-(void)createQueue2{
NSLog(@"將要延時...");
//延時幾秒操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//DISPATCH_TIME_NOW 從現在開始
//NSEC_PER_SEC 代表的是秒
//延時幾秒之後的操作
NSLog(@"延時執行");
});
[self performSelector:@selector(afterRun) withObject:nil afterDelay:2.0];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
線程組
//線程組 監聽線程
dispatch_group_t group = dispatch_group_create();
//創建全局隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"線程1執行完畢");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3.0];
NSLog(@"線程2執行完畢");
});
//前兩個線程結束後會通知這個線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"返回主線程");
});
阻塞線程組
dispatch_group_t group = dispatch_group_create();
//異步操作組
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i ++) {
//進入線程組
dispatch_group_enter(group);
[NSThread sleepForTimeInterval:1.f];
NSLog(@"線程 --- %d",i);
//離開線程組
dispatch_group_leave(group);
}
});
//目的:先執行完前面的線程 再執行後面的線程
//阻塞線程組
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//異步操作隊列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"另一個線程執行了");
});
-(void)createQueue1{
//重復執行某個線程
//第一個參數 重復執行的次數
dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t idx) {
NSLog(@”線程執行…”);
});
}
-(void)createQueue2{
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
NSLog(@"線程1");
});
dispatch_async(queue, ^{
NSLog(@"線程2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier...");
});
dispatch_async(queue, ^{
NSLog(@"線程3");
});
}
-(void)createQueue3{
//信號量
//創建信號量 參數為計數
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//等待接收信號 接收信號要寫在當前線程所有操作之前 計數-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"線程1執行...");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"線程2執行...");
//發送信號 在當前線程操作執行完畢之後 再發送信號 計數+1
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"線程3執行...");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"線程4執行...");
});
}