自定義線程池YYDispatchQueuePool

YYDispatchQueuePool原理:
通過維護(hù)一個(gè)上下文結(jié)構(gòu)體胯究,根據(jù)不同QOS優(yōu)先級創(chuàng)建串行隊(duì)列(隊(duì)列數(shù)不超過內(nèi)核)脂凶,因此在每個(gè)串行隊(duì)列下,線程都會(huì)按照先進(jìn)先出的順序去執(zhí)行任務(wù)御滩。通過這樣就可以控制線程數(shù)缴川。

隊(duì)列和線程之間的關(guān)系:
一個(gè)隊(duì)列由一個(gè)或多個(gè)任務(wù)組成,當(dāng)這些任務(wù)要開始執(zhí)行時(shí)额湘,系統(tǒng)會(huì)分別把他們分配到某個(gè)線程上去執(zhí)行卿吐。當(dāng)有多個(gè)系統(tǒng)核心時(shí),為了高效運(yùn)行缩挑,這些核心會(huì)將多個(gè)線程分配到各核心上去執(zhí)行任務(wù)但两。
對于一個(gè)并行隊(duì)列來說,其中的任務(wù)可能被分配到多個(gè)線程中去執(zhí)行供置,即這個(gè)并行隊(duì)列可能對應(yīng)多個(gè)線程谨湘。對于串行隊(duì)列,它每次對應(yīng)一個(gè)線程芥丧,這個(gè)線程可能不變紧阔,可能會(huì)被更換。每一時(shí)刻续担,一個(gè)線程都只能執(zhí)行一個(gè)任務(wù)擅耽。一個(gè)線程也可能是閑置或者掛起的,因此線程存在時(shí)不一定就在執(zhí)行任務(wù)物遇。

iOS8.0之后乖仇,隊(duì)列優(yōu)先級分為5種

  • NSQualityOfServiceUserInteractive:最高優(yōu)先級, 用于處理 UI 相關(guān)的任務(wù)
  • NSQualityOfServiceUserInitiated:次高優(yōu)先級, 用于執(zhí)行需要立即返回的任務(wù)
  • NSQualityOfServiceUtility:普通優(yōu)先級,主要用于不需要立即返回的任務(wù)
  • NSQualityOfServiceBackground:后臺(tái)優(yōu)先級询兴,用于處理一些用戶不會(huì)感知的任務(wù)
  • NSQualityOfServiceDefault:默認(rèn)優(yōu)先級乃沙,當(dāng)沒有設(shè)置優(yōu)先級的時(shí)候,線程默認(rèn)優(yōu)先級

下面是YYDispatchQueuePool里的處理

static inline qos_class_t NSQualityOfServiceToQOSClass(NSQualityOfService qos) {
    switch (qos) {
        case NSQualityOfServiceUserInteractive: return QOS_CLASS_USER_INTERACTIVE;
        case NSQualityOfServiceUserInitiated: return QOS_CLASS_USER_INITIATED;
        case NSQualityOfServiceUtility: return QOS_CLASS_UTILITY;
        case NSQualityOfServiceBackground: return QOS_CLASS_BACKGROUND;
        case NSQualityOfServiceDefault: return QOS_CLASS_DEFAULT;
        default: return QOS_CLASS_UNSPECIFIED;
    }
}

YYDispatchContext上下文結(jié)構(gòu)體的定義

typedef struct {
    const char *name;
    void **queues;
   //隊(duì)列數(shù)
    uint32_t queueCount;
   //計(jì)數(shù)器
    int32_t counter;
} YYDispatchContext;

YYDispatchContextCreate 上下文創(chuàng)建
YYDispatchContextRelease 上下文釋放
YYDispatchContextGetQueue 根據(jù)上下文獲取隊(duì)列
YYDispatchContextGetForQOS 根據(jù)QOS優(yōu)先級獲取上下文

YYDispatchContextCreate的實(shí)現(xiàn)

static YYDispatchContext *YYDispatchContextCreate(const char *name,
                                                 uint32_t queueCount,
                                                 NSQualityOfService qos) {
    YYDispatchContext *context = calloc(1, sizeof(YYDispatchContext));
    if (!context) return NULL;
    context->queues =  calloc(queueCount, sizeof(void *));
    if (!context->queues) {
        free(context);
        return NULL;
    }
   //根據(jù)優(yōu)先級創(chuàng)建隊(duì)列诗舰,控制隊(duì)列數(shù)
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
       //iOS8.0之后
        dispatch_qos_class_t qosClass = NSQualityOfServiceToQOSClass(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
           //這里創(chuàng)建的是serial queue串行隊(duì)列
          //不用concurrent queue警儒,主要是因?yàn)椴⑿嘘?duì)列會(huì)讓多個(gè)線程同時(shí)執(zhí)行,會(huì)大量占用CPU資源
            dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0);
            dispatch_queue_t queue = dispatch_queue_create(name, attr);
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    } else {
        //iOS8.0之前
        long identifier = NSQualityOfServiceToDispatchPriority(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(queue, dispatch_get_global_queue(identifier, 0));
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    }
    context->queueCount = queueCount;
    if (name) {
         context->name = strdup(name);
    }
    return context;
}

不使用concurrent queue的原因:避免concurrent queue下眶根,大量線程同時(shí)創(chuàng)建蜀铲、運(yùn)行、銷毀属百,占用主線程的CPU資源记劝,可能會(huì)導(dǎo)致卡頓等問題

下面描述引自YY大神iOS 保持界面流暢的技巧

大量的任務(wù)提交到后臺(tái)隊(duì)列時(shí),某些任務(wù)會(huì)因?yàn)槟承┰虮绘i住導(dǎo)致線程休眠族扰,或者被阻塞厌丑,concurrent queue 隨后會(huì)創(chuàng)建新的線程來執(zhí)行其他任務(wù)钳恕。當(dāng)這種情況變多時(shí),或者 App 中使用了大量 concurrent queue 來執(zhí)行較多任務(wù)時(shí)蹄衷,App 在同一時(shí)刻就會(huì)存在幾十個(gè)線程同時(shí)運(yùn)行忧额、創(chuàng)建、銷毀愧口。CPU 是用時(shí)間片輪轉(zhuǎn)來實(shí)現(xiàn)線程并發(fā)的睦番,盡管 concurrent queue 能控制線程的優(yōu)先級,但當(dāng)大量線程同時(shí)創(chuàng)建運(yùn)行銷毀時(shí)耍属,這些操作仍然會(huì)擠占掉主線程的 CPU 資源托嚣。

YYDispatchContextGetForQOS實(shí)現(xiàn)

static YYDispatchContext *YYDispatchContextGetForQOS(NSQualityOfService qos) {
    static YYDispatchContext *context[5] = {0};
    switch (qos) {
       //最高優(yōu)先級
        case NSQualityOfServiceUserInteractive: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
               //獲取內(nèi)核數(shù)、這里雖然總體能控制了線程數(shù)厚骗,但不能把CPU發(fā)揮到極致
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
             //最大限制了32個(gè)
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
            //創(chuàng)建上下文
                context[0] = YYDispatchContextCreate("com.ibireme.yykit.user-interactive", count, qos);
            });
            return context[0];
        } break;
       //...more 更多的級別實(shí)現(xiàn)基本都類似
        default: {
           //默認(rèn)優(yōu)先級
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[4] = YYDispatchContextCreate("com.ibireme.yykit.default", count, qos);
            });
            return context[4];
        } break;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末示启,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子领舰,更是在濱河造成了極大的恐慌夫嗓,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冲秽,死亡現(xiàn)場離奇詭異舍咖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锉桑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門排霉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人民轴,你說我怎么就攤上這事攻柠。” “怎么了后裸?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵瑰钮,是天一觀的道長。 經(jīng)常有香客問我轻抱,道長飞涂,這世上最難降的妖魔是什么旦部? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任祈搜,我火速辦了婚禮,結(jié)果婚禮上士八,老公的妹妹穿的比我還像新娘容燕。我一直安慰自己,他們只是感情好婚度,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布蘸秘。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醋虏。 梳的紋絲不亂的頭發(fā)上寻咒,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音颈嚼,去河邊找鬼毛秘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阻课,可吹牛的內(nèi)容都是我干的叫挟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼限煞,長吁一口氣:“原來是場噩夢啊……” “哼抹恳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起署驻,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤奋献,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后旺上,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秽荞,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年抚官,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扬跋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌节,死狀恐怖钦听,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倍奢,我是刑警寧澤朴上,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站卒煞,受9級特大地震影響痪宰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜畔裕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一衣撬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扮饶,春花似錦具练、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哥遮。三九已至,卻和暖如春陵究,著一層夾襖步出監(jiān)牢的瞬間眠饮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工铜邮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留君仆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓牲距,卻偏偏與公主長得像返咱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子牍鞠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容