iOS多線程GCD簡介(二)

在上一篇中揽趾,我們主要講了Dispatch Queue相關(guān)的內(nèi)容。這篇主要講一下一些和實際相關(guān)的使用實例酸休,Dispatch Groups和Dispatch Semaphore乞巧。

dispatch_after

在我們開發(fā)過程中經(jīng)常會用到在多少秒后執(zhí)行某個方法口蝠,通常我們會用這個- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay函數(shù)器钟。不過現(xiàn)在我們可以使用一個新的方法。

dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
    dispatch_after(delayTime, dispatch_get_main_queue(), ^{
        //do your task
    });

這樣我們就定義了一個延遲2秒后執(zhí)行的任務(wù)妙蔗。不過在這里有一點需要說明的是傲霸,無論你用的是- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay還是dispatch_after這個方法。并不是說在你指定的延遲后立即運行眉反,這些方法都是基于單線程的昙啄,它只是將你延遲的操作加入到隊列里面去。由于隊列里面都是FIFO寸五,所以必須在你這個任務(wù)之前的操作完成后才會執(zhí)行你的方法梳凛。這個延遲只是大概的延遲。如果你在主線程里面調(diào)用這個方法梳杏,如果你主線程現(xiàn)在正在處理一個非常耗時的任務(wù)韧拒,那么你這個延遲可能就會偏差很大。這個時候你可以再開個線程十性,在里面執(zhí)行你的延遲操作叭莫。

//放到全局默認(rèn)的線程里面,這樣就不必等待當(dāng)前調(diào)用線程執(zhí)行完后再執(zhí)行你的方法
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
    dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //do your task
    });

dispatch_once

這個想必大家都非常的熟悉烁试,這個在單例初始化的時候是蘋果官方推薦的方法。這個函數(shù)可以保證在應(yīng)用程序中只執(zhí)行指定的任務(wù)一次拢肆。即使在多線程的環(huán)境下執(zhí)行减响,也可以保證百分之百的安全靖诗。

    static id instance;
    static dispatch_once_t predicate;

    dispatch_once(&predicate, ^{
        //your init
    });

    return instance;
}

這里面的predicate必須是全局或者靜態(tài)對象。在多線程下同時訪問時支示,這個方法將被線程同步等待刊橘,直到指定的block執(zhí)行完成。

dispatch_apply

這個方法是執(zhí)行循環(huán)次數(shù)固定的迭代颂鸿,如果在并發(fā)的queue里面可以提高性能促绵。比如一個固定次數(shù)的for循環(huán)

for (int i = 0; i < 1000; i ++) {
        NSLog(@"---%d---", i);
    }

如果只是在一個線程里面或者在一個串行的隊列中是一樣的,一個個執(zhí)行嘴纺。
現(xiàn)在我們用dispatch_apply來寫這個循環(huán):

dispatch_apply([array count], defaultQueue, ^(size_t i) {
        NSLog(@"----%@---", array[i]);
    });
    NSLog(@"end");

這個方法執(zhí)行后败晴,它將像這個并發(fā)隊列中不斷的提交執(zhí)行的block。這個i是從0開始的栽渴,最后一個是[array count] - 1尖坤。

使用這個方法有幾個注意點:

  1. 這個方法調(diào)用的時候會阻塞當(dāng)前的線程,也就是上面的循環(huán)全部執(zhí)行完畢后闲擦,才會輸出end慢味。
  2. 在你使用這個任務(wù)進(jìn)行操作的時候,你應(yīng)該確保你要執(zhí)行的各個任務(wù)是獨立的墅冷,而且執(zhí)行順序也是無關(guān)緊要的纯路。
  3. 在你使用這個方法的時候,你還是要權(quán)衡下整體的性能的寞忿,如果你執(zhí)行的任務(wù)時間比線程切換的時間還短驰唬。那就得不償失了。

dispatch_group

在實際開發(fā)中罐脊,我們可能需要在一組操作全部完成后定嗓,才做其他操作。比如上傳一組圖片萍桌,或者下載多個文件宵溅。希望在全部完成時給用戶一個提示。如果這些操作在串行化的隊列中執(zhí)行的話上炎,那么你可以很明確的知道恃逻,當(dāng)最后一個任務(wù)執(zhí)行完成后,就全部完成了藕施。這樣的操作也并木有發(fā)揮多線程的優(yōu)勢寇损。我們可以在并發(fā)的隊列中進(jìn)行這些操作,但是這個時候我們就不知道哪個是最后一個完成的了裳食。這個時候我們可以借助dispatch_group:

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, defaultQueue, ^{
        //task1
        NSLog(@"1");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task2
        NSLog(@"2");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task3
        NSLog(@"3");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task4
        NSLog(@"4");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task5
        NSLog(@"5");
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"finish");
    });

我們首先創(chuàng)建一個group然后往里面加入我們要執(zhí)行的操作矛市,在dispatch_group_notify這個函數(shù)里面添加全部完成的操作。上面代碼執(zhí)行的時候诲祸,輸出的1浊吏,2而昨,3,4找田,5的順序是不一定的歌憨,但是輸出的finish一定是在1,2墩衙,3务嫡,4,5之后漆改。
對于添加到group的操作還有另外一個方法:

    dispatch_group_enter(group);
    dispatch_group_enter(group);

    dispatch_async(defaultQueue, ^{
        NSLog(@"1");
        dispatch_group_leave(group);
    });

    dispatch_async(defaultQueue, ^{
        NSLog(@"2");
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"finish");
    });

我們可以用dispatch_group_enter來表示添加任務(wù)心铃,dispatch_group_leave來表示有個任務(wù)已經(jīng)完成了。用這個方法一定要注意必須成雙成對籽懦。

線程同步

在多線程中一個比較重要的東西就是線程同步的問題于个。如果多個線程只是對某個資源只是讀的過程,那么就不存在這個問題了暮顺。如果某個線程對這個資源需要進(jìn)行寫的操作厅篓,那這個時候就會出現(xiàn)數(shù)據(jù)不一致的問題了。

使用dispatch_barrier_async

    __block NSString *strTest = @"test";

    dispatch_async(defaultQueue, ^{
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
    });
    dispatch_async(defaultQueue, ^{
        NSLog(@"--%@--3-", strTest);
    });
    dispatch_async(defaultQueue, ^{
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
    });

看看這個模擬的場景捶码,我們讓各個線程去訪問這個變量羽氮,其中有個操作是要修改這個變量。我們把第一個操作先判斷有木有改變惫恼,然后故意延遲一下档押,這個時候我們看下輸出結(jié)果:

2015-01-03 15:42:21.351 測試[1652:60015] --test--3-
2015-01-03 15:42:21.351 測試[1652:60013] --modify--4-
2015-01-03 15:42:21.351 測試[1652:60014] --test--1-
2015-01-03 15:42:22.355 測試[1652:60014] ====changed===

我們可以看到,再次判斷的時候祈纯,已經(jīng)被修改了令宿,如果我們在實際的業(yè)務(wù)中這樣去判斷某些關(guān)鍵性的變量,可能就會出現(xiàn)嚴(yán)重的問題腕窥。下面看看我們?nèi)绾问褂?code>dispatch_barrier_async來進(jìn)行同步:

 //并發(fā)隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"--%@--3-", strTest);
    });
    dispatch_barrier_async(concurrentQueue, ^{
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"--%@--5-", strTest);
    });

現(xiàn)在看下輸出結(jié)果:

2015-01-03 16:00:27.552 測試[1786:65947] --test--1-
2015-01-03 16:00:27.552 測試[1786:65965] --test--3-
2015-01-03 16:00:29.553 測試[1786:65947] --test--2-
2015-01-03 16:00:29.553 測試[1786:65947] --modify--4-
2015-01-03 16:00:29.553 測試[1786:65947] --modify--5-

現(xiàn)在我們可以發(fā)現(xiàn)操作4用dispatch_barrier_async加入操作后粒没,前面的操作3之前都操作完成之前這個strTest都沒有變。而后面的操作都是改變后的值簇爆。這樣我們的數(shù)據(jù)沖突的問題就解決了癞松。
現(xiàn)在說明下這個函數(shù)干的事情,當(dāng)這個函數(shù)加入到隊列后入蛆,里面block并不是立即執(zhí)行的响蓉,它會先等待之前正在執(zhí)行的block全部完成后,才執(zhí)行哨毁,并且在它之后加入到隊列中的block也在它操作結(jié)束后才能恢復(fù)之前的并發(fā)執(zhí)行枫甲。我們可以把這個函數(shù)理解為一條分割線,之前的操作,之后加入的操作言秸。還有一個點要說明的是這個queue必須是用dispatch_queue_create創(chuàng)建出來的才行软能。

使用Dispatch Semaphore

dispatch_semaphore_t 類似信號量,可以用來控制訪問某一資源訪問數(shù)量。
使用過程:

  1. 先創(chuàng)建一個Dispatch Semaphore對象举畸,用整數(shù)值表示資源的可用數(shù)量
  2. 在每個任務(wù)中,調(diào)用dispatch_semaphore_wait來等待
  3. 獲得資源就可以進(jìn)行操作
  4. 操作完后調(diào)用dispatch_semaphore_signal來釋放資源
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--3-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--5-", strTest);
        dispatch_semaphore_signal(semaphore);
    });

這樣我們一樣可以保證凳枝,線程的數(shù)據(jù)安全抄沮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岖瑰,隨后出現(xiàn)的幾起案子叛买,更是在濱河造成了極大的恐慌,老刑警劉巖蹋订,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件率挣,死亡現(xiàn)場離奇詭異,居然都是意外死亡露戒,警方通過查閱死者的電腦和手機(jī)椒功,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智什,“玉大人动漾,你說我怎么就攤上這事≤В” “怎么了旱眯?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長证九。 經(jīng)常有香客問我删豺,道長,這世上最難降的妖魔是什么愧怜? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任呀页,我火速辦了婚禮,結(jié)果婚禮上叫搁,老公的妹妹穿的比我還像新娘赔桌。我一直安慰自己,他們只是感情好渴逻,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布疾党。 她就那樣靜靜地躺著,像睡著了一般惨奕。 火紅的嫁衣襯著肌膚如雪雪位。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天梨撞,我揣著相機(jī)與錄音雹洗,去河邊找鬼香罐。 笑死,一個胖子當(dāng)著我的面吹牛时肿,可吹牛的內(nèi)容都是我干的庇茫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼螃成,長吁一口氣:“原來是場噩夢啊……” “哼旦签!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寸宏,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤宁炫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后氮凝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羔巢,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年罩阵,在試婚紗的時候發(fā)現(xiàn)自己被綠了竿秆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡永脓,死狀恐怖袍辞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情常摧,我是刑警寧澤搅吁,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站落午,受9級特大地震影響谎懦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溃斋,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一界拦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梗劫,春花似錦享甸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至走哺,卻和暖如春蚯嫌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工择示, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留束凑,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓栅盲,卻偏偏與公主長得像汪诉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子剪菱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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

  • 一蚓哩、多線程簡介: 所謂多線程是指一個 進(jìn)程 -- process(可以理解為系統(tǒng)中正在運行的一個應(yīng)用程序)中可以開...
    尋形覓影閱讀 1,013評論 0 6
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了构灸!去的時候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似岸梨。...
    Dely閱讀 9,226評論 21 42
  • 這是最近在讀的書喜颁。法頂禪師著,活在時間之外曹阔。這本書一直被我放在床頭半开,失眠的夜,或是有閒暇的下午赃份,躺在床上寂拆,隨手拿起...
    此刻是金__閱讀 242評論 3 0
  • 片片黃葉迎秋來,只覺炎夏未消亡抓韩。百轉(zhuǎn)千愁至纠永,莫言人心死,悠樂非我忘谒拴,紅塵萬千絲難斷…… 己所不欲勿施人尝江,花退殘...
    知味如水閱讀 204評論 0 0
  • 回答網(wǎng)友的一個問題炭序,男女交往中男性的核心競爭力是什么? 在不同階段苍日,男性的核心競爭力有不同的定義惭聂。如果是在大學(xué)期間...
    人生葵花寶典閱讀 1,691評論 4 14