1. 程式人生 > >iOS GCD中如何控制最大併發數

iOS GCD中如何控制最大併發數

//聯絡人:石虎  QQ: 1224614774暱稱:嗡嘛呢叭咪哄

一、概述

1、GCD併發的困擾
  • 在GCD中有兩種佇列,分別是序列佇列和併發佇列。在序列佇列中,同一時間只有一個任務在執行,不能充分利用多核 CPU 的資源,效率較低。

  • 併發佇列可以分配多個執行緒,同時處理不同的任務;效率雖然提升了,但是多執行緒的併發是用時間片輪轉方法實現的,執行緒建立、銷燬、上下文切換等會消耗CPU 資源。

  • 目前iPhone的處理器是多核(2個、4個),適當的併發可以提高效率,但是無節制地併發,如將大量任務不加思索就用併發佇列來執行,這隻會大量增加執行緒數,搶佔CPU資源,甚至會擠佔掉主執行緒的 CPU 資源(極端情況)。

  • 此外,提交給併發佇列的任務中,有些任務內部會有全域性的鎖(如 CoreText 繪製時的 CGFont 內部鎖),會導致執行緒休眠、阻塞;一旦這類任務多,併發佇列還需要建立新的執行緒來執行其他任務;這種情況下,執行緒數大量增加是避免不了的。

2、優雅的NSOperationQueue
  • NSOperationQueue是iOS提供的工作佇列,開發者只需要將任務封裝在NSOperation的子類(NSBlockOperation、NSInvocationOperation或自定義NSOperation子類)中,然後新增進NSOperationQueue佇列,佇列就會按照優先順序及工作的從屬依賴關係(如果有的話)組織執行。

  • NSOperationQueue中,已經考慮到了最大併發數的問題,並提供了maxConcurrentOperationCount屬性設定最大併發數(該屬性需要在任務新增到佇列中之前進行設定)。maxConcurrentOperationCount預設值是-1;如果值設為0,那麼不會執行任何任務;如果值設為1,那麼該佇列是序列的;如果大於1,那麼是並行的。

    NSOperationQueue *queue = [[NSOperationQueue alloc]init];queue.maxConcurrentOperationCount = 2;//新增Operation任務...

  • 第三方庫如SDWebImage庫和AFNetworking 中就是採用NSOperationQueue來控制最大併發數的。

說明:NSOperationQueue使用詳見多執行緒程式設計3 - NSOperationQueue 和 NSOperation

3、我們該怎麼辦
  • GCD多執行緒方案很優秀,在iOS 4 與 MacOS X 10.6之後,NSOperationQueue的底層就是用GCD來實現的。

  • NSOperationQueue在控制最大併發數上的確很方便,但是GCD也提供了某些機制可以實現控制最大併發數的效果。

  • 開發中NSOperationQueue和GCD都可以用,視場景而定(個人更喜歡用GCD)。

二、QSDispatchQueue方案

1、GCD的訊號量機制(dispatch_semaphore)
  • 訊號量是一個整型值,有初始計數值;可以接收通知訊號和等待訊號。當訊號量收到通知訊號時,計數+1;當訊號量收到等待訊號時,計數-1;如果訊號量為0,執行緒會被阻塞,直到訊號量大於0,才會繼續下去。

  • 使用訊號量機制可以實現執行緒的同步,也可以控制最大併發數。以下是如何控制最大併發數的程式碼。

dispatch_queue_t workConcurrentQueue = dispatch_queue_create(
"cccccccc", DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t serialQueue
= dispatch_queue_create("sssssssss",DISPATCH_QUEUE_SERIAL);dispatch
_semaphore_t semaphore = dispatch_semaphore_create(3);for (NSInteger
i = 0; i < 10; i++) { 
 dispatch_async(serialQueue, ^{   
   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);    
   dispatch_async(workConcurrentQueue, ^{      
    NSLog(@"thread-info:%@開始執行任務%d",[NSThread 
currentThread],(int)i);         
    sleep(1);        
    NSLog(@"thread-info:%@結束執行任務%d",[NSThread 
currentThread],(int)i);        
   dispatch_semaphore_signal(semaphore);});  
    });}
    NSLog(@"主執行緒...!");

說明:從執行結果中可以看出,雖然將10個任務都非同步加入了併發佇列,但是訊號量機制控制了最大執行緒併發數,始終是3個執行緒在執行任務。此外,這些任務也沒有阻塞主執行緒。

2、QSDispatchQueue方案的實現

1)直接在程式碼中使用GCD的訊號量,不夠優雅,程式碼也很冗餘;基於此,QSDispatchQueue方案出來了。(程式碼很簡單,一共兩個檔案)

2)QSDispatchQueue方法宣告如下:

//[email protected] QSDispatchQueue : NSObject#pragma mark 
- main queue + global queue/** 全域性併發佇列的最大併發數,預設4 */
+ (QSDispatchQueue *)mainThreadQueue;
+ (QSDispatchQueue *)defaultGlobalQueue;
+ (QSDispatchQueue *)lowGlobalQueue;
+ (QSDispatchQueue *)highGlobalQueue;
+ (QSDispatchQueue *)backGroundGlobalQueue;
#pragma mark [email protected] 
(nonatomic,assign,readonly)NSUInteger concurrentCount;
- (instancetype)init;
/** 預設最大併發數是1 @param queue 併發佇列 */
- (instancetype)initWithQueue:(dispatch_queue_t)queue;
/** @param queue 併發佇列 @param concurrentCount 最大併發數,應大於1 */
- (instancetype)initWithQueue:(dispatch_queue_t)queue

             concurrentCount:(NSUInteger)concurrentCount;//同步-
             (void)
             sync:(dispatch_block_t)block;//非同步- (void)async:
             (dispatch
             _block_t)block;@end

3、QSDispatchQueue方案的使用

dispatch_queue_t workConcurrentQueue = dispatch_queue_create(
"cccccccc",
DISPATCH_QUEUE_CONCURRENT);

QSDispatchQueue *queue = [[QSDispatchQueue alloc]initWithQueue:
workConcurrentQueue concurrentCount:3];
for (NSInteger i = 0; i < 10;
i++) {  
  [queue async:^{   
     NSLog(@"thread-info:%@開始執行任務%d",
[NSThread currentThread],(int)i);      
  sleep(1);       
 NSLog(@"thread-info:%@結束執行任務%d",
[NSThread currentThread],(int)i);    
}];
}NSLog(@"主執行緒任務...");

說明:從執行結果中來看,通過QSDispatchQueue方案也到達了最大執行緒併發數的目的。

  • 使用QSDispatchQueue方案,程式碼更簡潔,讓開發者不用去時刻注意訊號量的處理,只關注任務即可。

三、小結

  • 在iOS開發中,我們常將耗時任務提交給GCD的併發佇列,但是併發佇列並不會去管理最大併發數,無限制提交任務給併發佇列,會給效能帶來問題。

  • YYKit元件中的YYDispatchQueuePool 也能控制併發佇列的併發數;其思路是為不同優先順序建立和 CPU 數量相同的 serial queue,每次從 pool 中獲取 queue 時,會輪詢返回其中一個 queue。

  • QSDispatchQueue是使用訊號量讓併發佇列中的任務併發數得到抑制;YYDispatchQueuePool是讓一定數量的序列佇列代替併發佇列,避開了併發佇列不好控制併發數的問題。

    謝謝!!!

相關推薦

iOS GCD如何控制併發

//聯絡人:石虎  QQ: 1224614774暱稱:嗡嘛呢叭咪哄 一、概述 1、GCD併發的困擾 在GCD中有兩種佇列,分別是序列佇列和併發佇列。在序列佇列中,同一時間只有一個任務在執行,不能充分利用多核 CPU 的資源,效率較低。 併發佇列可以分配多

2018年最後一天 VsCode執行nodeJs程式碼的簡單方法 Pgsql和Mysql的對比 Tomcat的併發 Spring AOP不起作用原因

發現2017的隨筆總結依舊適用,想堅持每天寫點東西分享,但感覺每天能原創分享的內容真的不多,尤其是要把自己想分享的內容寫清楚也需要額外花費很多的時間,這讓本來平時就工作比較忙的自己疲於應付,於是乎就轉載自己看到的一些好的文章分享,甚至有些文章自己都沒完全看完就發,湊合著完成了任務,但自己的成就感很低。因此我不

mysql 檢視連線,狀態,併發

show status like '%max_connections%'; ##mysql最大連線數set global max_connections=1000 ##重新設定show variables like '%max_connections%'; ##查詢資料庫當前設定的最大連線數 show gl

瀏覽器同域名請求的併發限制

當我們在瀏覽網頁的時候,對瀏覽速度有一個重要的影響因素,就是瀏覽器的併發數量。併發數量簡單通俗的講就是,當瀏覽器網頁的時候同時工作的進行數量。   如果同時只有2個併發連線數數量,那網頁開啟的時候只能依賴於

(CSDN遷移) JAVA多執行緒實現-可控併發執行緒池(newFixedThreadPool)

上篇文章中介紹了單執行緒化執行緒池newSingleThreadExecutor,可控最大併發數執行緒池(newFixedThreadPool)與其最大的區別是可以通知執行多個執行緒,可以簡單的將newSingleThreadExecutor理解為newFixedThreadPool(1)。例如執行一下兩個程

Mysql 檢視連線,狀態 併發

-- show variables like '%max_connections%'; 檢視最大連線數 set global max_connections=1000 重新設定 mysql> show status like 'Threads%'; +---

找陣列絕對值,並保留規定的小數點數

#include using namespace std; #include<math.h> #include float date[8] = { -200.1,-40.5,80,99.367,0,-778.354,666,454.235 }; in

Mysql 檢視連線,狀態 併發 && 怎麼設定才合理

show status like '%max_connections%'; ##mysql最大連線數 set global max_connections=1000 ##重新設定 show variables like '%max_connections%'; ##查詢資料庫當前設定的最大連線數 show

Mysql 檢視連線,狀態 併發(贊)

1.show status    Threads_connected  當前的連線數    Connections  試圖連線到(不管是否成功)MySQL伺服器的連線數。    Max_used_connections  伺服器啟動後已經同時使用的連線的最大數量。 2.set GLOBAL max_con

Tomcat的效能與併發

當一個程序有 500 個執行緒在跑的話,那效能已經是很低很低了。Tomcat 預設配置的最大請求數是 150,也就是說同時支援 150 個併發,當然了,也可以將其改大。當某個應用擁有 250 個以上併發的時候,應考慮應用伺服器的叢集。具體能承載多少併發,需要看硬體的配置,C

使用Filter限制J2EE併發

在開發J2EE的過程往往需要限制併發請求量從而減少伺服器異常的可能性。而這些通常都是通過叢集手段或外部代理來實現的。本文主要介紹單個應用如何不依賴其他程式來解決這個問題。 J2EE的每個請求都是經由過濾器(責任鏈模式),Servlet來執行的,每個請求的進入都需要過濾器的准

oracle 併發 會話查詢

SQL> select count(*) from v$session #當前的連線數 SQL> Select count(*) from v$session where status='ACTIVE' #併發連線數 SQL> select value

oracle12c 報錯: ora01792 表或檢視為1000

安裝好Oracle12C,使用SQL語句進行查詢時,當查詢的列沒有達到1000,但是關聯的表過多,關聯表的總列數超過了1000的話,會報上面的錯誤。 解決方案如下:        用SysDba登入,執行以下這句話就可解決:alter system set "_fix_co

Apache 設定併發

prefork的工作原理是, 控制程序在最初建立“StartServers”個子程序後,為了滿足MinSpareServers設定的需要建立一個程序,等待一秒鐘,繼續建立兩 個,再等待一秒鐘,繼續建立四個……如此按指數級增加建立的程序數,最多達到每秒32個,直到滿足MinSpareServers設定的值為止

設定springboot自帶tomcat的連線併發

從原始碼來看,最大連線數和最大併發數預設是10000和200 可以通過工程下的application.yml配置檔案來改變這個值 server:   tomcat:     uri-enco

Oracle併發&License情況

檢視當前license情況 SQL> show parameter license; NAME TYPE VALUE ----------------------------------

iOS GCD併發控制

dispatch_semaphore_t sem = dispatch_semaphore_create(1); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 這兩句程式碼為什麼會閃退 最後訊號數量不對等, d

值並把它放到最後

創建 ++ and ati ofa () out arrays 之間 import java.util.Arrays;import java.util.Random;public class MaxOfArray { public static void main(S

ORA-01795: 列表表達式為 1000

條件數 express ora- bsp else number sql語句 截取 lec 系統報出一SQL異常,內容如下: java.sql.SQLException: ORA-01795: maximum number of expressions in a list

51Nod—1174 區間 線段樹模版

using ace pan struct 註意 truct logs mark mar 在大佬們題解的幫助下算是看懂了線段樹吧。。。在這mark下防一手轉頭就忘。 #include<iostream> #include<stdio.h> using