明天要給師弟開分享會,分享GCD。 好方,只理解一些皮毛拿什麼去裝。準備的時候順便把過程記錄下來。安全
術語 | 含義 |
---|---|
進程 | 打開一個App就是開啓一個進程。 |
線程 | 獨立執行的代碼段,一個線程同時間只能執行一個任務,反之多線程併發就能夠在同一時間執行多個任務。在iOS系統中,一個進程包含一個主線程,它的主要任務是處理UI。其餘線程稱爲子線程。 |
同步 | A執行完再執行B。 |
異步 | A和B能夠同時執行。 |
任務 | 能夠理解爲某一堆要執行的代碼。分爲同步執行任務和異步執行任務。用block定義。 |
同步執行任務 | 按進入順序執行的任務 |
異步執行任務 | 無論進入順序,能夠一塊兒執行 |
隊列 | 存聽任務的結構。分爲串行隊列和並行隊列。遵循先進先出。 |
隊列組 | 將多線程進行分組,最大的好處是可獲知全部線程的完成狀況。 |
串行隊列 | 線程執行只能依次逐一前後有序的執行。 |
並行隊列 | 指兩個或多個事件在同一時刻發生。多核CUP同時開啓多條線程供多個任務同時執行,互不干擾。 |
併發 | 指兩個或多個事件在同一時間間隔內發生。能夠在某條線程和其餘線程之間反覆屢次進行上下文切換,看上去就好像一個CPU可以而且執行多個線程同樣。實際上是僞異步。 |
回頭看以爲有必要在這簡單說明多線程 多核 併發並行的區別 和 子線程和主線程的聯繫。bash
最近玩了個遊戲叫《Inside》,戴着頭盔就能操縱機器人,感受不管是玩法仍是遊戲劇情都超適合類比線程。 用這個舉個例子。 假如你是國王,拿到了一張藏寶圖,但這個寶藏要到每個地點才能得知下一個地點的信息(電路中內存地址)。因而你就操縱機器人A去找,找到後帶回來。機器人A的路線就是一條線程。 當機器人A還在路程上,你又獲得一張藏寶圖。你這時候派機器人B去找,找到帶回來。這時候機器人B的路線就是另外一條線程。 以上就是多線程。 這時候,只要你週期足夠短,輪流戴頭盔a和頭盔b,,看上去就像你同時在操縱機器人A和機器人B。這就叫作併發!裝出來的。 某一天,你的頭快搖傻了。因而乎你長出了第二個頭。(對應着雙核CPU),這時候就是名副其實地同時操縱。這就叫並行,必需要多頭怪才擁有這技能。 但若是又操縱第三個機器人,這時候只能再來回戴了,又要併發了。 A找到並回到了城堡把結果帶回給你,你才發現你也是個機器人(主線程)。其餘機器人帶回寶藏後就能夠拜拜了,但就算還有沒有寶藏在路上,你都不能拜拜,必須保持呼吸(runloop)。 這就是子線程和主線程的聯繫。 子線程的任務所有完成後,最終會回到主線程。主線程中運行着runloop網絡
就是把任務加到隊列中 隊列能夠本身新建。 系統也有 全局併發隊列和主隊列。多線程
#pragma mark - 建立隊列
// 建立隊列
// 第一個參數 隊列名稱
// 第二個參數的做用:串行(DISPATCH_QUEUE_SERIAL)、並行(DISPATCH_QUEUE_CONCURRENT)。
dispatch_queue_t queue = dispatch_queue_create("net.Hsusue.testQueue", DISPATCH_QUEUE_CONCURRENT);
* 經常使用的系統併發隊列——全局併發隊列
//程序默認的隊列級別,通常不要修改,DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//HIGH
dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//LOW
dispatch_queue_t globalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//BACKGROUND
dispatch_queue_t globalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 獲取主隊列有特別函數(是個串行隊列)
// dispatch_queue_t queue = dispatch_get_main_queue();
#pragma mark - 建立任務加到隊列中
// 同步執行任務建立方法
dispatch_sync(queue, ^{
// 這裏放同步執行任務代碼
});
// 異步執行任務建立方法
dispatch_async(queue, ^{
// 這裏放異步執行任務代碼
});
複製代碼
仍是不能忘了《Inside》的例子。併發
兩種待辦任務表(對應隊列) 一種是多個機器人對多個寶藏,先入先出發。(對應並行隊列) 另外一種是一個機器人對有順序找的多個寶藏。(對應串行隊列) 特殊的 強行本身去作的任務表。 (對應主隊列)app
你有兩類事情(對應任務) 一類是吃喝拉撒,一有須要就本身立刻去作,總不能懶到讓機器人幫忙吧。(對應着同步執行任務) 另外一類是尋寶,要機器人去作,出發前要點時間給機器人充電。(對應着異步執行任務)異步
代碼中, 輸出@"1"對應着吃喝拉撒
async
- (void)viewDidLoad {
[super viewDidLoad];
[self asyncConcurrent];
NSLog(@"1");
}
//異步執行 + 並行隊列
- (void)asyncConcurrent{
//建立一個並行隊列
dispatch_queue_t queue = dispatch_queue_create("標識符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用異步函數封裝三個任務
dispatch_async(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
多個機器人找多個寶藏,完成程度和你的吃喝拉撒沒必然前後順序。
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
[self asyncSerial];
NSLog(@"1---%@", [NSThread currentThread]);
}
//異步 + 串行隊列
- (void)asyncSerial{
//建立一個串行隊列
dispatch_queue_t queue = dispatch_queue_create("標識符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用異步函數封裝三個任務
dispatch_async(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
一個機器人等有序寶藏圖拼接好後,就出發了。和你吃喝拉撒沒前後順序。
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
[self syncConcurrent];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步 + 並行隊列
- (void)syncConcurrent{
//建立一個並行隊列
dispatch_queue_t queue = dispatch_queue_create("標識符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用同步函數封裝三個任務
dispatch_sync(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
一要吃喝拉撒就本身立刻去作。因此不等@「end」輸出就先作完了。最後再@「1」。有着必然前後順序。
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
[self syncSerial];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步 + 串行隊列
- (void)syncSerial{
//建立一個串行隊列
dispatch_queue_t queue = dispatch_queue_create("標識符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用異步函數封裝三個任務
dispatch_sync(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
此次更過度了,試圖讓一個機器人幫本身拉三次尿。。。但機器人作不到。
一要吃喝拉撒就本身立刻去作。因此不等@「end」輸出就先作完了。最後再@「1」。有着必然前後順序。ide
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
[self asyncMain];
NSLog(@"1---%@", [NSThread currentThread]);
}
//異步 + 主隊列
- (void)asyncMain{
//獲取主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用異步函數封裝三個任務
dispatch_async(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
和異步 + 串行隊列區別就是不開啓新線程。 函數
讓機器人充電準備,因此本身先吃喝拉撒完。直到@"1"。 而後你發現這件事是在強制本身作的任務表上,因而就本身一件接一件作了。- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
[self syncMain];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步+主隊列(死鎖)
- (void)syncMain{
//獲取主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用同步函數封裝三個任務
dispatch_sync(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
複製代碼
這裏認真分析一下死鎖的緣由,不用那個例子了。 先說點計算機組成原理的知識,雖然我也學得很爛。
計算機指令包括操做碼和地址碼。
每一個函數進入都會記住進入的地址碼,return時就會回去。
上面主隊列在主隊列中加了任務。 實質在同一個同步串行隊列中,再使用該串行隊列同步的執行任務。
[self syncMain]這是主隊列作(出)的事(同步且未作完)。根據先進先出,主隊列頭是syncMain。而後假設這裏的內存地址是1。
dispatch_sync(queue, ^{
NSLog(@"任務A---%@", [NSThread currentThread]);
});
// 假設運行時此處內存地址爲1
複製代碼
添加了一個block到主隊列尾部,要等主隊列頭synMain執行完才能執行。 原本應該執行追加任務B,可是電路上的地址並無回來,由於dispatch_sync
要執行完block才reutrn。 由於被代碼被黑盒子包起來了,大膽猜想一下。 假設內存地址爲2
// 調用時記住進入地址爲1
dispatch_sync {
// block執行完才return
// 運行時此處內存地址爲2
if( block() ) { // block執行完
return;//返回到進入地址
}
}
複製代碼
因而代碼能夠當作 卡在了該函數內部,內存地址爲2處。 沒有回到1處,天然就不會追加任務B。
上面說了不少種方法,禁止死鎖狀況開發中是很容易記住的。 但其餘組合,即便想的時候能想懂,但也仍是很混亂。 根據我我的經驗,平常開發中先從宏觀上想是否須要耗時(耗時放到子線程),是否有序。 一般是須要和主線程同時執行(開新線程,即異步執行任務)纔會用到GCD。 多是開發經驗不夠。
從子線程,異步返回主線程更新UI。 隊列經常使用全局並行隊列。 由於要下載圖片耗時,並且具備網絡不穩定性,因此放到子線程。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3948453733,2367168123&fm=27&gp=0.jpg"]];
UIImage *image = [UIImage imageWithData:imgData];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
UIImageView *imgView = [[UIImageView alloc]initWithImage:image];
[imgView setFrame:CGRectMake(0, 0, 200, 200)];
[imgView setCenter:self.view.center];
[self.view addSubview:imgView];
});
});
複製代碼
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
// [self syncMain];
[self groupTest];
NSLog(@"1---%@", [NSThread currentThread]);
}
- (void)groupTest {
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_t groupQueue = dispatch_group_create();
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"並行任務1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"並行任務2");
});
dispatch_group_notify(groupQueue, mainQueue, ^{
NSLog(@"groupQueue中的任務 都執行完成,回到主線程更新UI");
});
NSLog(@"next task");
}
複製代碼
1.
dispatch_group_t groupQueue = dispatch_group_create();
用於生成隊列組 2.生成隊列時加上前綴_guoup 3.
dispatch_group_notify
這個函數用以處理其餘隊列完成的塊。
static dispatch_once_ onceToken;
dispatch_once( &onceToken,^{
對象A =[ [對象A alloc] init];
});
複製代碼
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
// [self syncMain];
// [self groupTest];
[self barrier];
NSLog(@"1---%@", [NSThread currentThread]);
}
// 欄柵函數
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任務A---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務B---%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"欄柵函數---%@",[NSThread currentThread]);
});
// 換成同步執行也同樣
// dispatch_barrier_sync(queue, ^{
// NSLog(@"欄柵函數---%@",[NSThread currentThread]);
// });
dispatch_async(queue, ^{
NSLog(@"任務C---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務D---%@",[NSThread currentThread]);
});
}
複製代碼
能夠這麼理解
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t i) {
NSLog(@"%zd----%@", i, [NSThread currentThread];
}
複製代碼
加鎖方法
方法一 互斥鎖(同步鎖)@synchronized(鎖對象) {
// 須要鎖定的代碼
}
複製代碼
判斷的時候鎖對象要存在,若是代碼中只有一個地方須要加鎖,大多都使用self做爲鎖對象,這樣能夠避免單獨再建立一個鎖對象。 方法二:自旋鎖 用到屬性修飾原子屬性nonatomic
和 atomic非原子屬性
力薦第三篇,看了不少瞎說的,就這篇真實!!!