首先,先看看進程和線程的概念。程序員
圖1.1編程
這一塊不難理解,重點點下他們的幾個重要區別:多線程
1,地址空間和資源:進程能夠申請和擁有系統資源,線程不行。資源進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。閉包
2,通訊:進程間須要用到IPC(這個能夠誰總結開個課),線程能夠直接讀寫進程的數據段來通訊(須要涉及鎖,下面會簡單講到)。併發
3,調度和切換:線程快,進程慢。app
好了,切回主題,iOS多線程技術,通常有三個,NSThread,NSOperation/NSOperationQueue,GCD(Grand Central Dispatch),好,首先先拋出一些概念。異步
以上就是對於這三種技術的簡單介紹,接着再來介紹下幾個基本的概念,async
隊列:隊列是先進先出(FIFO)結構的,在iOS中,隊列主要的任務是負責線程的建立、回收工做,不論什麼隊列和什麼任務都不須要程序員參與,減輕了程序員的工做。有GCD隊列和自定義隊列。根據這些,咱們實際上能夠操做四種隊列,全局隊列,主隊列,串行隊列,並行隊列。函數
任務:分爲同步任務和異步任務。同步任務:必須按照順序執行的任務,異步任務:執行順序不必定,哪一個先搶佔到資源哪一個先執行(我試過將多個異步任務併發執行,系統會將這些任務分配給若干個線程,而後由線程調度執行,分配到同一線程的任務仍是按順序執行的)。優化
因而乎,將這些隊列和任務組合,就會出現8種組合,趕忙投票吧,分享會上用代碼來詳細講解下這8種組合的效果。
下面簡單介紹下以上三個技術在代碼中的使用:
NSThread 使用它常常是用來查看當前線程:[NSThread currentThread](用來判斷是否在主線程執行也很方便),由於其餘的確實很差用,也不推薦用。
NSOperation/NSOperationQueue 雖然說是蘋果基於GCD實現的面相對象的線程技術,可是感受用起來沒有GCD方便,下面簡單介紹下它的幾個用法
NSOperation 對應任務 有如下三種用法
1 NSInvocationOperation
2 NSBlockOperation
3 自定義NSOperation 子類
1 和 2 我給出初始化方法 你們就一眼能知道區別和用法了。
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];// 調用start方法執行操做op操做
[op start];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task0---%@", [NSThread currentThread]);
}];
[op start];
恩 其實就是調用的是block仍是方法的區別了。注意 start後是指在當前線程同步執行任務。
自定義子類也說下
主要操做就是繼承NSOperation,而後在.m文件中實現- (void)mian方法,而後運行start的時候就會運行main方法裏的代碼。
另外 任務提供執行完成的回調
op.completeBlock能夠監聽一個操做執行完畢的時刻,這個block裏面能夠添加一些咱們須要執行的操做,這個block裏面的操做仍然是在子線程執行,但不必定和被監聽的操做在同一個線程
接着講下NSOperationQueue
NSOperationQueue 對應隊列 且是並行隊列。
先解決個疑惑,如何使用串行隊列?NSOperationQueue提供了一個設置最大併發數的方法setMaxConcurrentOperationCount: 只要將最大併發數設置爲1,就是串行隊列了。
首先 NSOperationQueue 能夠獲取到主隊列 或者本身建立。
NSOperationQueue 提供 addOperation和addOperationWithBlock方法 名字取得很好,一眼就能夠看出用法了,前者用來添加任務,後者用block的方式來添加任務。
同時提供cancal單個任務和cancelAllOperations全部任務的方式,不能cancel正在執行的任務。
同時提供獲取掛起狀態和設置掛起狀態的方法。
isSuspended : 判斷是否掛起
setSuspended: YES表示掛起,NO表示恢復
一樣 不能對正在執行的任務設置。
任務間的執行前後,能夠設置依賴來排序。
[op2 addDependency:op1];
Op1執行完成後才執行op2.
其實簡單的操做用NSOperation比起GCD來要美觀一下,我的感受。
最後講重點GCD 我以爲它之因此強大是由於它提供了不止前面這些功能還提供了一些經常使用的額外功能,下面我來一一講解。
幾個簡單的和前面功能相似的
dispatch_get_global_queue全局隊列並行隊列
dispatch_get_main_queue主隊列主線程中的惟一隊列
dispatch_queue_create自定義隊列DISPATCH_QUEUE_SERIAL串DISPATCH_QUEUE_CONCURRENT並行
dispatch_sync 同步線程
dispatch_async 異步線程
這些用法和前面相似 到時8個組合用GCD簡單演示下,至關於兩個一塊兒理解了。
之因此說GCD比NSOperation強大,就是他不只能實現NSOperation能實現的,還有不少很好用的方法,下面一一講解:
dispatch_once 一次性代碼 使用dispatch_once保證代碼只被執行一次 使用場景:單例對象的初始化 這個你們接觸的不少了 很少講了
dispatch_group_t 隊列組 使用這個能夠很好的控制任務調度
咱們常常有需求 須要在一個頁面請求多個接口 當多個接口都完成後執行刷新UI的操做 這個時候咱們的操做通常都是多個異步返回的時候各自判斷 其實用dispatch_group_t 能夠很好的解決這個問題
思路以下
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 異步取數據A
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 異步取數據B
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操做都執行完畢後,回到主線程刷新UI
});
dispatch_group_notify是隊列組中全部任務都完成後會發送的通知。
思考下:咱們的業務場景常常能用到這個思路,你們有沒例子的?
//建立隊列
self.isolationQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT);
//改變setter
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
key = [key copy];
//確保全部barrier都是async異步的
dispatch_barrier_async(self.isolationQueue, ^(){
if (count == 0) {
[self.counts removeObjectForKey:key];
} else {
self.counts[key] = @(count);
}
});
}
這是網上找的代碼 實際上synchronized能夠更好的完成這個鎖操做,這是後話啦,有空再分析這個。實際上他的用法也比較多,到時搞個demo分享下。
用來替換對前一次計算沒有依賴的for循環會優化不少,例如
for (int i = 0; i < 999 ; i++) {
dispatch_async(concurrentQueue, ^{
NSLog(@"wrong %d",i);
//do something hard
});
}
改用dispatch_apply 來處理
dispatch_apply(999, concurrentQueue, ^(size_t i){
NSLog(@"correct %zu",i);
//do something hard
});
Dispatch IO 文件操做
用到的方式是多個線程去讀取不一樣的數據塊,而後再合併。
Dispatch source 這個也是程序優化的一道利器,且支持多種場景,對於程序優化頗有幫助,這塊能夠大力研究下,用Dispatch source來取代一些回調函數能夠有效的提升程序執行的效率,codereview下咱們的代碼,應該有可以用上這塊的(到時奉上)。
dispatch_semaphore_wait等待信號量須要dispatch_semaphore_signal執行後才能跳過。配合使用能夠解決不少複雜的線程問題。
發揮下想象力:經過這個信號量,是否是用很好的方式解決咱們請求等待同步的問題?
最後稍微講下鎖
synchronized就可解決大部分的問題了 給出ElearningConfig中的代碼示例建議使用
實際上就是這段被{}包起來的代碼只會被一個線程調用,而鎖就是self對象。意思就是,執行代碼塊的時候會對self上鎖,當下一個線程須要對代碼塊調用時,須要等待上一個程序解鎖self才能夠。
好啦,更多精彩內容,咱們分享會見!