iOS GCD基本用法

GCD全稱為Grand Central Dispatch,是libdispatch的市場名稱,而libdispatch是Apple的一個庫教寂,其為并發(fā)代碼在iOS和OS X的多核硬件上執(zhí)行提供支持杀狡。確切地說GCD是一套低層級的C API,通過 GCD酪夷,開發(fā)者只需要向隊列中添加一段代碼塊(block或C函數(shù)指針)榴啸,而不需要直接和線程打交道。GCD在后端管理著一個線程池捶索,它不僅決定著你的代碼塊將在哪個線程被執(zhí)行插掂,還根據(jù)可用的系統(tǒng)資源對這些線程進行管理。這樣通過GCD來管理線程腥例,從而解決線程被創(chuàng)建的問題

GCD 核心概念

GCD編程的核心就是dispatch隊列辅甥,dispatch block的執(zhí)行最終都會放進某個隊列中去進行。

1燎竖、GCD中的隊列類型

1璃弄、The main queue(主線程串行隊列): 與主線程功能相同,提交至Main queue的任務(wù)會在主線程中執(zhí)行构回,Main queue 可以通過dispatch_get_main_queue()來獲取夏块。
2、Global queue(全局并發(fā)隊列): 全局并發(fā)隊列由整個進程共享纤掸,有高脐供、中(默認)、低借跪、后臺四個優(yōu)先級別政己。Global queue 可以通過調(diào)用dispatch_get_global_queue函數(shù)來獲取(可以設(shè)置優(yōu)先級)
3掏愁、Custom queue (自定義隊列): 可以為串行歇由,也可以為并發(fā)卵牍。Custom queue 可以通過dispatch_queue_create()來獲取沦泌;
4糊昙、Group queue (隊列組):將多線程進行分組,最大的好處是可獲知所有線程的完成情況谢谦。Group queue 可以通過調(diào)用dispatch_group_create()來獲取释牺,通過dispatch_group_notify,可以直接監(jiān)聽組里所有線程完成情況。

2他宛、GCD相關(guān)概念
1船侧、Dispatch Objects

盡管GCD是純C語言的,但它被組建成面向?qū)ο蟮娘L(fēng)格厅各。GCD對象被稱為dispatch object, 所有的dispatch objects都是OC對象.镜撩,就如其他OC對象一樣,當(dāng)開啟了ARC(automatic reference counting)時,dispatch objects的retain和release都會自動執(zhí)行队塘。而如果是MRC的話袁梗,dispatch objects會使用dispatch_retain和dispatch_release這兩個方法來控制引用計數(shù)。

2憔古、Serial & Concurrent

串行任務(wù)就是每次只有一個任務(wù)被執(zhí)行遮怜,并發(fā)任務(wù)就是在同一時間可以有多個任務(wù)被執(zhí)行。

3鸿市、Synchronous & Asynchronous

同步函數(shù)意思是在完成了它預(yù)定的任務(wù)后才返回锯梁,在任務(wù)執(zhí)行時會阻塞當(dāng)前線程。而異步函數(shù)則是任務(wù)會完成但不會等它完成焰情,所以異步函數(shù)不會阻塞當(dāng)前線程陌凳,會繼續(xù)去執(zhí)行下一個函數(shù)。

4内舟、Concurrency & Parallelism

并發(fā)的意思就是同時運行多個任務(wù)合敦。這些任務(wù)可能是以在單核 CPU 上以分時(時間共享)的形式同時運行,也可能是在多核 CPU 上以真正的并行方式來運行验游。然后為了使單核設(shè)備也能實現(xiàn)這一點充岛,并發(fā)任務(wù)必須先運行一個線程,執(zhí)行一個上下文切換耕蝉,然后運行另一個線程或進程崔梗。并行則是真正意思上的多任務(wù)同時運行。

5垒在、Context Switch

Context Switch即上下文切換炒俱,一個上下文切換指當(dāng)你在單個進程里切換執(zhí)行不同的線程時存儲與恢復(fù)執(zhí)行狀態(tài)的過程。這個過程在編寫多任務(wù)應(yīng)用時很普遍,但會帶來一些額外的開銷权悟。

6、Dispatch Queues

GCD dispatch queues是一個強大的執(zhí)行多任務(wù)的工具推盛。Dispatch queue是一個對象峦阁,它可以接受任務(wù),并將任務(wù)以先進先出(FIFO)的順序來執(zhí)行耘成。Dispatch queue可以并發(fā)的或串行的執(zhí)行任意一個代碼塊榔昔,而且并發(fā)任務(wù)會像NSOperationQueue那樣基于系統(tǒng)負載來合適地并發(fā)進行,串行隊列同一時間則只執(zhí)行單一任務(wù)瘪菌。Dispatch queues內(nèi)部使用的是線程撒会,GCD 管理這些線程,并且使用Dispatch queues的時候师妙,我們都不需要自己創(chuàng)建線程诵肛。Dispatch queues相對于和線程直接通信的代碼優(yōu)勢是:Dispatch queues使用起來特別方便,執(zhí)行任務(wù)更加有效率默穴。

GCD 簡單用法

1怔檩、獲取主隊列
    //獲取主隊列
    dispatch_queue_t main_queue = dispatch_get_main_queue();
    //主隊列是串行隊列;里面的任務(wù)會串行執(zhí)行
    dispatch_async(main_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"A:%d",i);
            sleep(1);
        }
    });
    dispatch_async(main_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"B:%d",i);
            sleep(1);
        }
    });

2、獲取全局隊列
//獲取全局隊列
    dispatch_queue_t global_queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //全局隊列中添加兩個任務(wù)蓄诽,全局隊列是并發(fā)隊列薛训。
    dispatch_async(global_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"C:%d",i);
            sleep(1);
        }
    });
    dispatch_async(global_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"D:%d",i);
            sleep(1);
        }
    });

3、自定義串行隊列
//創(chuàng)建串行隊列
    dispatch_queue_t serial_queue = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"%@",serial_queue.description);
    //添加任務(wù)
    dispatch_async(serial_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"E:%d",i);
            sleep(1);
        }
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"F:%d",i);
            sleep(1);
        }
    });

4仑氛、自定義并發(fā)隊列
 //創(chuàng)建一個并發(fā)隊列
    dispatch_queue_t concurrent_queue = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    //添加并發(fā)任務(wù)
    dispatch_async(concurrent_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"G:%d",i);
            sleep(1);
        }
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        for (int i=0; i<3; i++) {
            NSLog(@"H:%d",i);
            sleep(1);
        }
    });

5乙埃、延遲執(zhí)行一個任務(wù)
 //延遲多少秒
    double delaySeconds=5;
    //創(chuàng)建時間
    dispatch_time_t delay_time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC));
    //執(zhí)行延遲任務(wù)
    dispatch_after(delay_time, dispatch_get_main_queue(), ^{
        NSLog(@"hello world");
    });

6、調(diào)度監(jiān)聽任務(wù)完成
//獲取并發(fā)隊列
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //創(chuàng)建調(diào)度組
    dispatch_group_t one_group = dispatch_group_create();
    //通過調(diào)度組發(fā)起并發(fā)任務(wù)
    dispatch_group_async(one_group, global_queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"I:%d",i);
            sleep(1);
        }
    });
    dispatch_group_async(one_group, global_queue, ^{
        for (int i=0; i<4; i++) {
            NSLog(@"J:%d",i);
            sleep(1);
        }
    });
    dispatch_group_async(one_group, global_queue, ^{
        for (int i=0; i<5; i++) {
            NSLog(@"K:%d",i);
            sleep(1);
        }
    });
    //調(diào)度組完成任務(wù)通知
    dispatch_group_notify(one_group, dispatch_get_main_queue(), ^{
        NSLog(@"one_group完成任務(wù)");
    });
}


7锯岖、調(diào)度等待任務(wù)完成
//獲取并發(fā)隊列
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //創(chuàng)建調(diào)度組
    dispatch_group_t one_group = dispatch_group_create();
    //進入調(diào)度組
    dispatch_group_enter(one_group);
    //發(fā)起并發(fā)任務(wù)
    dispatch_async(global_queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"L:%d",i);
            sleep(1);
        }
    });
    dispatch_async(global_queue, ^{
        for (int i=0; i<4; i++) {
            NSLog(@"M:%d",i);
            sleep(1);
        }
    });
    dispatch_async(global_queue, ^{
        for (int i=0; i<5; i++) {
            NSLog(@"N:%d",i);
            sleep(1);
        }
        //離開調(diào)度組
        dispatch_group_leave(one_group);
    });
    //調(diào)度組等待:等待過程中介袜,下面的代碼不會被執(zhí)行
    dispatch_group_wait(one_group, DISPATCH_TIME_FOREVER);
    NSLog(@"等到了!");

GCD常見用法和應(yīng)用場景

1、dispatch_async
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
   // 一個異步的任務(wù)嚎莉,例如網(wǎng)絡(luò)請求米酬,耗時的文件操作等等
   ...
   dispatch_async(dispatch_get_main_queue(), ^{
       // UI刷新
       ...
   });
});

這種用法非常常見,比如開啟一個異步的網(wǎng)絡(luò)請求趋箩,待數(shù)據(jù)返回后返回主隊列刷新UI赃额;又比如請求圖片,待圖片返回刷新UI等等叫确。

2跳芳、dispatch_after
// NSEC_PER_SEC,每秒有多少納秒竹勉。
// USEC_PER_SEC飞盆,每秒有多少毫秒。
// NSEC_PER_USEC,每毫秒有多少納秒吓歇。
// DISPATCH_TIME_NOW 從現(xiàn)在開始
// DISPATCH_TIME_FOREVE 永久

dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
    // 在queue里面延遲執(zhí)行的一段代碼
    ...
});

這為我們提供了一個簡單的延遲執(zhí)行的方式孽水,比如在view加載結(jié)束延遲執(zhí)行一個動畫等等。

3城看、dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只執(zhí)行一次的任務(wù)
    ...
});

可以使用其創(chuàng)建一個單例

4女气、dispatch_group
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    // 異步任務(wù)1
});

dispatch_group_async(group, queue, ^{
    // 異步任務(wù)2
});


dispatch_group_notify(group, mainQueue, ^{
    // 任務(wù)完成后,在主隊列中做一些操作
    ...
});

上述的一種方式测柠,可以適用于自己維護的一些異步任務(wù)的同步問題

5炼鞠、 dispatch_semaphore_signal(信號量)
//傳遞的參數(shù)是信號量最初值,下面例子的信號量最初值是1
 dispatch_semaphore_t signal = dispatch_semaphore_create(1);

    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

       // 當(dāng)信號量是0的時候,dispatch_semaphore_wait(signal, overTime);這句代碼會一直等待直到overTime超時.
//這里信號量是1 所以不會在這里發(fā)生等待.
        dispatch_semaphore_wait(signal, overTime);
        NSLog(@"需要線程同步的操作1 開始");
        sleep(2);
        NSLog(@"需要線程同步的操作1 結(jié)束");
       long signalValue = dispatch_semaphore_signal(signal);//這句代碼會使信號值 增加1 
//并且會喚醒一個線程去開始繼續(xù)工作,如果喚醒成功,那么返回一個非零的數(shù),如果沒有喚醒,那么返回 0

        NSLog(@"%ld",signalValue);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(signal, overTime);
        NSLog(@"需要線程同步的操作2");
        dispatch_semaphore_signal(signal);
        long signalValue = dispatch_semaphore_signal(signal);

        NSLog(@"%ld",signalValue);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(signal, overTime);
        NSLog(@"需要線程同步的操作3");
        dispatch_semaphore_signal(signal);
        long signalValue = dispatch_semaphore_signal(signal);

        NSLog(@"%ld",signalValue);
    });
利用 dispatch_semaphore_t signal 組織一個并發(fā)數(shù)是10 的一個多線程工作隊列.
dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    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);
//注意這里信號量從10開始遞減,并不會阻塞循環(huán).循環(huán)10次,遞減到0的時候,開始阻塞.
        NSLog(@"-------");
        dispatch_group_async(group, queue, ^{
            NSLog(@"%i",i);
            sleep(1);
            dispatch_semaphore_signal(semaphore);
        });//創(chuàng)建一個新線程,并在線程結(jié)束后,發(fā)送信號量,通知阻塞的循環(huán)繼續(xù)創(chuàng)建新線程.
    }
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
如此循環(huán)就形成了對并發(fā)的控制,如上就是一個并發(fā)數(shù)為10的一個線程隊列轰胁。

實戰(zhàn):利用 dispatch_semaphore_t signal 組織一個生產(chǎn)消費模式
 __block int product = 0;
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //消費者隊列

        while (1) {
            if(!dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER))){
////非 0的時候,就是成功的timeout了,這里判斷就是沒有timeout   成功的時候是 0

                NSLog(@"消費%d產(chǎn)品",product);
                product--;
            };
        }
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //生產(chǎn)者隊列   
        while (1) {

                sleep(1); //wait for a while
                product++;
                NSLog(@"生產(chǎn)%d產(chǎn)品",product);
                dispatch_semaphore_signal(sem);
        }

    });

6谒主、 dispatch_suspend和dispatch_resume
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暫停隊列queue
dispatch_resume(queue);  //恢復(fù)隊列queue
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赃阀,隨后出現(xiàn)的幾起案子霎肯,更是在濱河造成了極大的恐慌,老刑警劉巖凹耙,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姿现,死亡現(xiàn)場離奇詭異,居然都是意外死亡肖抱,警方通過查閱死者的電腦和手機备典,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來意述,“玉大人提佣,你說我怎么就攤上這事』绯纾” “怎么了拌屏?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長术荤。 經(jīng)常有香客問我倚喂,道長,這世上最難降的妖魔是什么瓣戚? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任端圈,我火速辦了婚禮,結(jié)果婚禮上子库,老公的妹妹穿的比我還像新娘舱权。我一直安慰自己,他們只是感情好仑嗅,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布宴倍。 她就那樣靜靜地躺著张症,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸵贬。 梳的紋絲不亂的頭發(fā)上俗他,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音阔逼,去河邊找鬼拯辙。 笑死,一個胖子當(dāng)著我的面吹牛颜价,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诉濒,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼周伦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了未荒?” 一聲冷哼從身側(cè)響起专挪,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎片排,沒想到半個月后寨腔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡率寡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年迫卢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冶共。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡乾蛤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捅僵,到底是詐尸還是另有隱情家卖,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布庙楚,位于F島的核電站上荡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馒闷。R本人自食惡果不足惜酪捡,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窜司。 院中可真熱鬧沛善,春花似錦、人聲如沸塞祈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尤蛮,卻和暖如春媳友,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背产捞。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工醇锚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坯临。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓焊唬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親看靠。 傳聞我的和親對象是個殘疾皇子赶促,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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

  • GCD筆記 總結(jié)一下多線程部分,最強大的無疑是GCD,那么先從這一塊部分講起. Dispatch Queue的種類...
    jins_1990閱讀 761評論 0 1
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時候我都想好了最壞的可能(胃癌)挟炬,之前在網(wǎng)上查的癥狀都很相似鸥滨。...
    Dely閱讀 9,239評論 21 42
  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 832評論 0 0
  • 該用什么來救贖我罪惡的靈魂 在炎陽高照的夏日里 卻急切渴望春天的來臨 歡欣的忘我 陶醉成爛泥 人生 不該追求些什么...
    成長的心閱讀 134評論 0 1
  • 也許是我膽小婿滓,謹(jǐn)小慎微,害怕一切潛在的危險因子粥喜,所以一直以來極力地去避免它們凸主,保護那個容易動情容易依賴愛沉浸愛自欺...
    v小姐要記得多讀書閱讀 239評論 0 0