在iOS開發(fā)的道路上,多線程的重要性不言而喻. 大部分我們都停留在基礎的使用上面.缺乏高級應用. 缺乏提升,是因為我們面對他太少,復雜的事情重復做,復雜的事務基礎化. 差距就是這樣拉開了 ------- 偉大的樓主
言歸正傳: 今天講講GCD的高級應用之信號量篇
一, 信號量的本質(zhì):
信號量的本質(zhì)是數(shù)據(jù)操作鎖, 它本身不具有數(shù)據(jù)交換的功能,而是通過控制其他的通信資源來實現(xiàn)進程間通信,它本身只是一種外部資源的標識。信號量在此過程中負責數(shù)據(jù)操作的互斥、同步等功能.
二: 信號量的工作原理
由于信號量只能進行兩種操作等待和發(fā)送信號,即P(sv)和V(sv),他們的行為是這樣的:
P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進程的執(zhí)行
V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1.
舉個例子,就是 兩個進程共享信號量sv,一旦其中一個進程執(zhí)行了P(sv)操作,它將得到信號量,并可以進入臨界區(qū),使sv減1。而第二個進程將被阻止進入臨界區(qū),因為 當它試圖執(zhí)行P(sv)時,sv為0,它會被掛起以等待第一個進程離開臨界區(qū)域并執(zhí)行V(sv)釋放信號量,這時第二個進程就可以恢復執(zhí)行。
三: iOS中GCD的信號量函數(shù)解析:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
這行代碼創(chuàng)建了一個信號量,同時指明了最多有2個資源可以訪問該"臨界區(qū)域"
dispatch_semaphore_signal(semaphore)
這行代碼 提高信號量 , 信號量計數(shù) + 1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
這行代碼 降低信號量 , 信號量計數(shù) - 1
特別注意 當信號為0(零),在執(zhí)行 dispatch_semaphore_wait 語句時,信號量計數(shù)小于0 ,阻塞當前線程.
四: GCD的信號量應用場景: 控制最大并發(fā)量, 控制資源的同步訪問,如數(shù)據(jù)訪問,網(wǎng)絡同步加載.
例如我有這樣的一段代碼,假設需求是控制兩個網(wǎng)絡的執(zhí)行順序 如想讓請求一完成之后,在進行網(wǎng)絡請求二,然后在進行網(wǎng)絡請求N (實現(xiàn)的方式有多種多樣)在此處主要討論GCD semaphore 信號量的使用:
首先大家看看這段代碼帶來的問題
-(void)testSemaphore{
NSLog(@"current1:%@",[NSThread currentThread]);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[AKkaHttpTool Post:@"https://api.douban.com/v2/book/1220562" parameters:@{@"":@""} success:^(id _Nullable responseObject) {
dispatch_semaphore_signal(semaphore);
} failure:^(NSError * _Nullable error, NSInteger statusCode) {
dispatch_semaphore_signal(semaphore);
}];
NSLog(@"你會來這兒嗎1");
NSLog(@"current1:%@",[NSThread currentThread]);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //等待信號,當信號總量少于0 的時候就會一直等待 ,否則就可以正常的執(zhí)行,并讓信號總量-1
NSLog(@"你會來這兒嗎2");
[AKkaHttpTool Post:@"https://api.douban.com/v2/book/1220562" parameters:@{@"":@""} success:^(id _Nullable responseObject) {
NSLog(@"resqueue2:");
} failure:^(NSError * _Nullable error, NSInteger statusCode) {
}];
}
下面我用一張截圖進行說明,主要是用于說明 dispatch_semaphore_wait 會阻塞當前線程

接下來這三個網(wǎng)絡請求使用GCD信號量實現(xiàn)同步,并且不阻塞主線程
- (IBAction)gcd2:(id)sender {
dispatch_queue_t queue = dispatch_queue_create("AkSemaphore", NULL);
dispatch_async(queue, ^{
NSLog(@"current1:%@",[NSThread currentThread]);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[AKkaHttpTool Post:@"https://api.douban.com/v2/book/1220562" parameters:@{@"":@""} success:^(id _Nullable responseObject) {
dispatch_semaphore_signal(semaphore);
NSLog(@"resqueue1:");
} failure:^(NSError * _Nullable error, NSInteger statusCode) {
dispatch_semaphore_signal(semaphore);
}];
NSLog(@"你會來這兒嗎1");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //等待信號,當信號總量少于0 的時候就會一直等待 ,否則就可以正常的執(zhí)行,并讓信號總量-1
NSLog(@"你會來這兒嗎2");
[AKkaHttpTool Post:@"https://api.douban.com/v2/book/1220562" parameters:@{@"":@""} success:^(id _Nullable responseObject) {
NSLog(@"resqueue2:");
dispatch_semaphore_signal(semaphore);
} failure:^(NSError * _Nullable error, NSInteger statusCode) {
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //等待信號,當信號總量少于0 的時候就會一直等待 ,否則就可以正常的執(zhí)行,并讓信號總量-1
NSLog(@"你會來這兒嗎3");
[AKkaHttpTool Post:@"https://api.douban.com/v2/book/1220562" parameters:@{@"":@""} success:^(id _Nullable responseObject) {
NSLog(@"resqueue3:");
} failure:^(NSError * _Nullable error, NSInteger statusCode) {
}];
});
}

接下來講一下控制網(wǎng)絡的并發(fā)訪問 :
假如現(xiàn)在有一個這樣的需求,需要先下載50張圖片, 一般異步會開啟新的線程,但過多的線程 數(shù)與項目的性能是成反比的 . 所以控制并發(fā),提高性能則尤為重要 : 實例代碼如下
- (void)testGCD3{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i=0;i<100 ; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"i = %d",i);
//此處模擬一個 異步下載圖片的操作
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
}
代碼講解如下圖:
