iOS多線程之GCD半抱、GCD處理多任務(wù)的網(wǎng)絡(luò)請求脓恕、多讀單寫

在軟件開發(fā)中使用多線程可以大大地提高用戶體驗,提高效率代虾。Grand Central Dispatch(CGD)則是C語言的一套多線程開發(fā)框架进肯,相比NSThread和NSOperation,GCD更加高效棉磨,并且線程由系統(tǒng)管理江掩,會自動運行多核運算。因為這些優(yōu)勢乘瓤,GCD是Apple推薦給開發(fā)者使用的首選多線程解決方案环形。

1、GCD的調(diào)度機制

GCD框架中一個很重要的概念是調(diào)度隊列衙傀,我們對線程的操作實際上是由調(diào)度隊列完成的抬吟。我們只需要將要執(zhí)行的任務(wù)添加到合適的隊列中即可。在GCD框架中统抬,有如下三種類型的調(diào)度隊列火本。

1.1主隊列

其中的任務(wù)在主線程中執(zhí)行,因為其會阻塞主線程聪建,所以是一個串行的隊列钙畔。可以通過下面的方法得到:

dispatch_get_main_queue();

1.2全局并行隊列

隊列中任務(wù)的執(zhí)行嚴格按照先進先出的模式進行金麸。如果是串行的隊列擎析,則當(dāng)一個任務(wù)結(jié)束后,才會開啟另一個任務(wù)挥下,如果是并行隊列揍魂,則任務(wù)的開啟順序和添加順序是一致的。系統(tǒng)為iOS應(yīng)用自動創(chuàng)建了4個全局共享的并發(fā)隊列棚瘟。使用下面的函數(shù)獲得:

dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>);

上面函數(shù)的第一個參數(shù)是這個隊列的ID现斋,系統(tǒng)的4個全局隊列默認的優(yōu)先級不同,這個參數(shù)可填寫的定義如下:

#define DISPATCH_QUEUE_PRIORITY_HIGH 2 //優(yōu)先級別最高的全局隊列
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0//優(yōu)先級別中等的全局隊列
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//優(yōu)先級別較低的全局隊列
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后臺的全局隊列偎蘸,優(yōu)先級別最低

這個函數(shù)的第二個參數(shù)是一個預(yù)留參數(shù)庄蹋,我們可以傳NULL.

1.3自定義隊列

上面的兩種隊列都是系統(tǒng)為我們創(chuàng)建好的,我們只需要獲取到他們禀苦,添加任務(wù)即可蔓肯。當(dāng)然我們也可以創(chuàng)建自己的隊列遂鹊,包含串行和并行的振乏。使用如下方法來創(chuàng)建:

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

其中第一個參數(shù)是這個隊列的名字,第二個參數(shù)決定創(chuàng)建的是串行還是并行隊列秉扑。填寫DISPATCH_QUEUE_SERIAL或NULL創(chuàng)建串行隊列慧邮,填寫DISPATCH_QUEUE_CONCURRENT創(chuàng)建并行隊列调限。

2、添加任務(wù)到調(diào)度隊列中

使用dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)函數(shù)或者dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)函數(shù)來同步或異步的執(zhí)行任務(wù)误澳。示例如下:

- (void)creatGCDQueue {
    //創(chuàng)建一個串行的隊列
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    //向隊列中添加同步任務(wù)1
    dispatch_sync(queue, ^{
        NSLog(@"%@:task1",[NSThread currentThread]);
    });
    //向隊列中添加異步任務(wù)2
    dispatch_async(queue, ^{
        NSLog(@"%@:task2",[NSThread currentThread]);
    });
    
}

//打印信息:


image.png

上面的代碼創(chuàng)建了一個串行的自定義隊列耻矮,并且向隊列中添加了一個同步的任務(wù)和一個異步的任務(wù)。需要注意忆谓,這里的同步和異步指的是針對當(dāng)前代碼運行所在的線程而言的裆装。
從打印信息可以看出,同步的任務(wù)是在主線程中執(zhí)行倡缠,異步的任務(wù)是在單獨的線程中執(zhí)行哨免,由于我們創(chuàng)建的調(diào)度隊列是串行的,因此先開啟了任務(wù)1昙沦,后開啟了任務(wù)2.

只有當(dāng)調(diào)度隊列是并行琢唾,而且向隊列中添加的任務(wù)也是異步的時候,多任務(wù)才會實現(xiàn)并行異步執(zhí)行盾饮。

實現(xiàn)如下:

- (void)creatGCDQueue {
    //創(chuàng)建一個并行的隊列
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    //向隊列中添加異步任務(wù)1
    dispatch_async(queue, ^{
        for (int i = 0; i < 15; i ++) {
            NSLog(@"%@ = %d:task1",[NSThread currentThread],i);
        }
    });
    //向隊列中添加異步任務(wù)2
    dispatch_async(queue, ^{
        for (int i = 0; i < 15; i ++) {
            NSLog(@"%@ = %d:task2",[NSThread currentThread],i);
        }    });
    
}

3采桃、使用隊列組

通過前面的學(xué)習(xí),我們現(xiàn)在已經(jīng)可以運用隊列多線程執(zhí)行任務(wù)了丘损,但是GCD的強大之處遠遠不止如此普办。看下面的例子号俐。
如果有3個任務(wù)A泌豆、B、C吏饿,其中A與B是沒有關(guān)系的踪危,他們可以并行執(zhí)行,C必須是A猪落、B都結(jié)束之后才能執(zhí)行贞远,當(dāng)然,實現(xiàn)這樣的邏輯并不困難笨忌,使用KVO就可以實現(xiàn)蓝仲,但是如果使用隊列處理這樣的邏輯,則代碼會更加清晰簡單官疲。
可以使用dispatch_group_create()創(chuàng)建一個隊列組袱结,使用如下函數(shù)將隊列添加到隊列組中:

void dispatch_group_async(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);

隊列中的隊列是異步執(zhí)行的,示例如下:

- (void)creatGCDGroup {
    //創(chuàng)建一個隊列組
    dispatch_group_t group = dispatch_group_create();
    //創(chuàng)建一個異步隊列
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    //添加任務(wù)
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%@ = %d:task1",[NSThread currentThread],i);
        }
    });
    
    //添加任務(wù)
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%@ = %d:task2",[NSThread currentThread],i);
        }
    });
    
    //阻塞線程途凫,直到前面的隊列任務(wù)執(zhí)行完成
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    for (int i = 0; i < 10; i ++) {
        NSLog(@"%@ = %d:over",[NSThread currentThread],i);
    }
}

打印結(jié)果如下:


image.png

以上代碼完美的實現(xiàn)了我們的任務(wù)依賴需求垢夹,可以看出GCD的強大了吧,復(fù)雜的任務(wù)邏輯關(guān)系因為GCD變得十分清晰簡單维费。

4果元、GCD對循環(huán)任務(wù)的處理

說到循環(huán)促王,除了常規(guī)的while循環(huán),for循環(huán)外而晒,for-in也是開發(fā)中常用的一種循環(huán)方式蝇狼。for-in循環(huán)通常來進行數(shù)組或字典的遍歷,這種遍歷通常不關(guān)心循環(huán)執(zhí)行的順序倡怎。使用GCD迅耘,配合設(shè)備的多核運算技術(shù),我們可以將這種循環(huán)遍歷的性能提升到極致监署,示例如下:

- (void)creatGCDApply {
    dispatch_apply(20, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
        NSLog(@"%@:%zu",[NSThread currentThread],i);
    });
}

打印信息如下:


image.png

從打印信息可以看出豹障,循環(huán)是由多個不同的線程完成的,比如我們的設(shè)備是8核的CPU焦匈。因此每個線程單獨在一個核執(zhí)行血公,這將循環(huán)的運行效率提升到了極致。大大提高了運行速率缓熟。

5累魔、GCD中的消息與信號

5.1Dispatch Source

在GCD框架中提供了dispatch_source_t類型的對象,dispatch_source_t類型的對象可以用來傳遞和接收某個消息够滑。在任一線程上調(diào)用它的一個函數(shù) dispatch_source_merge_data 后垦写,會執(zhí)行 Dispatch Source 事先定義好的句柄(可以把句柄簡單理解為一個 block )。
這個過程叫 Custom event 彰触,用戶事件梯投。是 dispatch source 支持處理的一種事件。簡單地說况毅,這種事件是由你調(diào)用 dispatch_source_merge_data 函數(shù)來向自己發(fā)出的信號分蓖。
示例如下:

- (void)creatGCDSource {
    //創(chuàng)建一個數(shù)據(jù)對象,DISPATCH_SOURCE_TYPE_DATA_ADD的含義表示當(dāng)數(shù)據(jù)變化時相加
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    //設(shè)置響應(yīng)分派源事件的block尔许,在分派源指定的隊列上運行
    dispatch_source_set_event_handler(source, ^{
        
        NSLog(@"%lu:sec",dispatch_source_get_data(source));//得到分派源的數(shù)據(jù)
        dispatch_async(dispatch_get_main_queue(), ^{
            //更新UI
        });
        
    });
    //啟動
    dispatch_resume(source);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        //網(wǎng)絡(luò)請求
        //向分派源發(fā)送事件么鹤,需要注意的是,不可以傳遞0值(事件不會被觸發(fā))味廊,同樣也不可以傳遞負數(shù)蒸甜。
        dispatch_source_merge_data(source, 1);
    });

}

注意:DISPATCH_SOURCE_TYPE_DATA_ADD是將所有觸發(fā)結(jié)果相加,最后統(tǒng)一執(zhí)行響應(yīng)余佛,但是加入sleepForTimeInterval后柠新,如果interval的時間越長,則每次觸發(fā)都會響應(yīng)辉巡,但是如果interval的時間很短恨憎,則會將觸發(fā)后的結(jié)果相加后統(tǒng)一觸發(fā)。這在更新UI時很有用红氯,比如更新進度條時框咙,沒必要每次觸發(fā)都響應(yīng),因為更新時還有其他的用戶操作(用戶輸入痢甘,觸碰等)喇嘱,所以可以統(tǒng)一觸發(fā)

比如我們寫一個進度條的示例:

- (void)creatGCDSource {
    //1、指定DISPATCH_SOURCE_TYPE_DATA_ADD塞栅,做成Dispatch Source(分派源)者铜。設(shè)定Main Dispatch Queue 為追加處理的Dispatch Queue
       dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
       
       __block NSUInteger totalComplete = 0;
       
       dispatch_source_set_event_handler(source, ^{
           
           //當(dāng)處理事件被最終執(zhí)行時,計算后的數(shù)據(jù)可以通過dispatch_source_get_data來獲取放椰。這個數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會被重置作烟,所以totalComplete的值是最終累積的值。
           NSUInteger value = dispatch_source_get_data(source);
           
           totalComplete += value;
           
           NSLog(@"進度:%@", @((CGFloat)totalComplete/100));
           
           NSLog(@":large_blue_circle:線程號:%@", [NSThread currentThread]);
       });
       
       //分派源創(chuàng)建時默認處于暫停狀態(tài)砾医,在分派源分派處理程序之前必須先恢復(fù)拿撩。
       dispatch_resume(source);
       
       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
       
       //2、恢復(fù)源后如蚜,就可以通過dispatch_source_merge_data向Dispatch Source(分派源)發(fā)送事件:
    
       dispatch_async(queue, ^{
       
           for (NSUInteger index = 0; index < 100; index++) {
       
               dispatch_source_merge_data(source, 1);
       
               NSLog(@":recycle:線程號:%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
       
               sleep(0.1);
           }
       });

}

5.2压恒、信號量 singer

信號量是GCD中一個很重要的概念,他的用法與消息的傳遞有所類似错邦,其本示例代碼如下:

- (void)creatGCDSinger {
    //創(chuàng)建一個信號探赫,其中的參數(shù)是信號的初始值
    dispatch_semaphore_t singer = dispatch_semaphore_create(0);
    //發(fā)送信號,信號量+1
    dispatch_semaphore_signal(singer);
    //等待信號撬呢,當(dāng)信號量大于0時伦吠,執(zhí)行后面的代碼,否則等待魂拦,第二個參數(shù)為等待的超時時長毛仪,下面設(shè)置的為一直等待
    dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
    NSLog(@"singer");
}

注意,dispatch_semaphore_wait函數(shù)會阻塞當(dāng)前線程芯勘,在主線程中要慎用潭千。通過發(fā)送信號函數(shù):dispatch_semaphore_signal(),可以使信號量+1借尿,每次執(zhí)行過等待信號后刨晴,信號量會-1,如此路翻,我們可以很方便地控制不同隊列中方法的執(zhí)行流程狈癞。

5.2.1限制線程的最大并發(fā)數(shù)
- (void)creatGCDSinger {
    //創(chuàng)建一個信號,其中的參數(shù)是信號的初始值
    dispatch_semaphore_t singer = dispatch_semaphore_create(2);
    for (int i = 0; i < 15; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            //等待信號茂契,當(dāng)信號量大于0時蝶桶,執(zhí)行后面的代碼,否則等待掉冶,第二個參數(shù)為等待的超時時長真竖,下面設(shè)置的為一直等待
            dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
            //doing
            sleep(1);
            //發(fā)送信號脐雪,信號量+1
            dispatch_semaphore_signal(singer);
        });
    }
}

如上述代碼可知,總共異步執(zhí)行15個任務(wù)恢共,但是由于我們設(shè)置了值為2的信號量战秋,每一次執(zhí)行任務(wù)的時候信號量都會先-1,而在任務(wù)結(jié)束后使信號量加1讨韭,當(dāng)信號量減到0的時候脂信,說明正在執(zhí)行的任務(wù)有2個,這個時候其它任務(wù)就會阻塞透硝,直到有任務(wù)被完成時狰闪,這些任務(wù)才會執(zhí)行。

注意濒生,信號量的正常的使用順序是先降低(dispatch_semaphore_wait)然后再提高(dispatch_semaphore_signal)埋泵,這兩個函數(shù)通常成對使用。

5.2.2阻塞發(fā)請求的線程

有些時候罪治,我們需要阻塞發(fā)送請求的線程秋泄,比如在多個請求回調(diào)后統(tǒng)一操作的需求,而這些請求之間并沒有順序關(guān)系规阀,且這些接口都會另開線程進行網(wǎng)絡(luò)請求的恒序。一般地,這種多線程完成后進行統(tǒng)一操作的需求都會使用隊列組(dispatch_group_t)來完成谁撼,但是由于是異步請求歧胁,沒等其異步回調(diào)之后,請求的線程就結(jié)束了厉碟,為此喊巍,就需要使用信號量來阻塞住發(fā)請求的線程。實現(xiàn)代碼如下:

- (void)creatGCDSinger {
    //創(chuàng)建線程組
    dispatch_group_t group = dispatch_group_create();
    //獲取隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //任務(wù)1
    dispatch_group_async(group, queue, ^{
       //請求1
        [self request1];
    });
    
    //任務(wù)2
    dispatch_group_async(group, queue, ^{
       //請求2
        [self request2];
    });
    
    //任務(wù)3
    dispatch_group_async(group, queue, ^{
       //請求3
        [self request3];
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
         NSLog(@"-------所有網(wǎng)絡(luò)請求已請求完成-------");
     });
}

- (void)request1 {
    //創(chuàng)建信號量箍鼓,并設(shè)置為0崭参,信號量本質(zhì)是資源數(shù),為0表示用完款咖,需要等待
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    //模擬網(wǎng)絡(luò)請求-異步
    //每次網(wǎng)絡(luò)請求成功或失敗后何暮,都讓信號量+1,表示釋放當(dāng)前資源铐殃,其他線程可以搶占了
    [[KNetRequestManager share] getSomeData:^{
        //網(wǎng)絡(luò)請求成功海洼,發(fā)送信號
      dispatch_semaphore_signal(sema);
    } errorBlock:^{
        //網(wǎng)絡(luò)請求失敗,發(fā)送信號
        dispatch_semaphore_signal(sema);
    }];
    //如果信號量為0富腊,表示沒有資源可用坏逢,便一直等待,不再往下執(zhí)行.只有當(dāng)網(wǎng)絡(luò)請求成功或失敗時,才會往下走
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

- (void)request2 {
    //創(chuàng)建信號量是整,并設(shè)置為0肖揣,信號量本質(zhì)是資源數(shù),為0表示用完浮入,需要等待
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    //模擬網(wǎng)絡(luò)請求-異步
    //每次網(wǎng)絡(luò)請求成功或失敗后龙优,都讓信號量+1,表示釋放當(dāng)前資源舵盈,其他線程可以搶占了
    [[KNetRequestManager share] getSomeData:^{
        //網(wǎng)絡(luò)請求成功,發(fā)送信號
      dispatch_semaphore_signal(sema);
    } errorBlock:^{
        //網(wǎng)絡(luò)請求失敗球化,發(fā)送信號
        dispatch_semaphore_signal(sema);
    }];
    //如果信號量為0秽晚,表示沒有資源可用,便一直等待筒愚,不再往下執(zhí)行
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

- (void)request3 {
    //創(chuàng)建信號量赴蝇,并設(shè)置為0,信號量本質(zhì)是資源數(shù)巢掺,為0表示用完句伶,需要等待
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    //模擬網(wǎng)絡(luò)請求-異步
    //每次網(wǎng)絡(luò)請求成功或失敗后,都讓信號量+1陆淀,表示釋放當(dāng)前資源考余,其他線程可以搶占了
    [[KNetRequestManager share] getSomeData:^{
        //網(wǎng)絡(luò)請求成功,發(fā)送信號
      dispatch_semaphore_signal(sema);
    } errorBlock:^{
        //網(wǎng)絡(luò)請求失敗轧苫,發(fā)送信號
        dispatch_semaphore_signal(sema);
    }];
    //如果信號量為0楚堤,表示沒有資源可用,便一直等待含懊,不再往下執(zhí)行
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

當(dāng)然身冬,我們也可以使用dispatch_group_enter和dispatch_group_leave來實現(xiàn)同樣的功能:

- (void)creatGCDSinger {
    //創(chuàng)建線程組
    dispatch_group_t group = dispatch_group_create();
    //創(chuàng)建一個并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("group.queue", DISPATCH_QUEUE_CONCURRENT);
    //任務(wù)1
     dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
       //請求1
        [self request1WithGroup:group];
    });
    
    //任務(wù)2
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
       //請求2
        [self request2WithGroup:group];
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
         NSLog(@"-------所有網(wǎng)絡(luò)請求已請求完成-------");
     });
}

- (void)request1WithGroup:(dispatch_group_t)group {
    //模擬網(wǎng)絡(luò)請求-異步
    [[KNetRequestManager share] getSomeData:^{
        //網(wǎng)絡(luò)請求成功,調(diào)用level
      dispatch_group_leave(group);
    } errorBlock:^{
         //網(wǎng)絡(luò)請求失敗岔乔,調(diào)用level
        dispatch_group_leave(group);
    }];

}

- (void)request2WithGroup:(dispatch_group_t)group
    //模擬網(wǎng)絡(luò)請求-異步
    [[KNetRequestManager share] getSomeData:^{
        //網(wǎng)絡(luò)請求成功酥筝,調(diào)用level
      dispatch_group_leave(group);
    } errorBlock:^{
         //網(wǎng)絡(luò)請求失敗,調(diào)用level
        dispatch_group_leave(group);
    }];
}
5.2.3信號量控制網(wǎng)絡(luò)請求順序
- (void)creatGCDSinger {
    //創(chuàng)建semp
    dispatch_semaphore_t semp = dispatch_semaphore_create(1);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //任務(wù)1
    dispatch_async(queue, ^{
        //信號量-1
        dispatch_semaphore_wait(semp, DISPATCH_TIME_FOREVER);
        //模擬網(wǎng)絡(luò)請求
        //模擬網(wǎng)絡(luò)請求-異步
        //每次網(wǎng)絡(luò)請求成功或失敗后雏门,都讓信號量+1嘿歌,表示釋放當(dāng)前資源,其他線程可以搶占了
        [[KNetRequestManager share] getSomeData:^{
            //網(wǎng)絡(luò)請求成功茁影,發(fā)送信號
          dispatch_semaphore_signal(sema);
        } errorBlock:^{
            //網(wǎng)絡(luò)請求失敗搅幅,發(fā)送信號
            dispatch_semaphore_signal(sema);
        }];
    });
    //任務(wù)2
    dispatch_async(queue, ^{
        //信號量-1
        dispatch_semaphore_wait(semp, DISPATCH_TIME_FOREVER);
        //模擬網(wǎng)絡(luò)請求
        //模擬網(wǎng)絡(luò)請求-異步
        //每次網(wǎng)絡(luò)請求成功或失敗后,都讓信號量+1呼胚,表示釋放當(dāng)前資源茄唐,其他線程可以搶占了
        [[KNetRequestManager share] getSomeData:^{
            //網(wǎng)絡(luò)請求成功,發(fā)送信號
          dispatch_semaphore_signal(sema);
        } errorBlock:^{
            //網(wǎng)絡(luò)請求失敗,發(fā)送信號
            dispatch_semaphore_signal(sema);
        }];
    });
}

6沪编、隊列的掛起和開啟

在GCD框架中還提供了暫停與開始任務(wù)隊列的方法呼盆,使用下面的函數(shù)可以將隊列或隊列組暫時掛起和開啟:

//掛起隊列或隊列組
void dispatch_suspend(dispatch_object_t object);
//開啟隊列或隊列組
void dispatch_resume(dispatch_object_t object);

注意:在暫停隊列時,隊列中正在執(zhí)行的任務(wù)并不會中斷蚁廓,未開啟的任務(wù)會被掛起访圃。

7、數(shù)據(jù)存儲的線程安全問題-多度單寫

在進行多線程編程時相嵌,或許總會遇到這一類問題:數(shù)據(jù)的競爭與線程的安全腿时。這些問題如果通過程序手動來控制,則難度將會非常大饭宾。CGD同樣為我們簡單地解決了這樣的問題批糟。
首先,如果只是在讀取數(shù)據(jù)看铆,而不對數(shù)據(jù)做任何修改徽鼎,那么我們并不需要處理安全問題,可以讓多個任務(wù)同時進行讀取弹惦》裼伲可是如果要對數(shù)據(jù)進行寫操作,那么在同一時間棠隐,我們就必須只能有一個任務(wù)在寫石抡,CGD中有一個方法幫我們完美地解決了這個問題,示例如下:

- (void)creatCGDReadAndWriter {
    //創(chuàng)建一個隊列
    dispatch_queue_t queue = dispatch_queue_create("oneQueue", DISPATCH_QUEUE_CONCURRENT);
    //多個任務(wù)同時執(zhí)行讀操作
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"read1:%d",i);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"read2:%d",i);
        }
    });
    
    //執(zhí)行寫操作
    /*
     下面這個函數(shù)在加入隊列時不會執(zhí)行助泽,會等待已經(jīng)開始的異步執(zhí)行全部完成后再執(zhí)行汁雷,并且在執(zhí)行時會阻塞其他任務(wù)
     當(dāng)執(zhí)行完成后,其他任務(wù)重新進入異步執(zhí)行
     */
    dispatch_barrier_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
             NSLog(@"writer:%d",i);
        }
    });
    //績效執(zhí)行異步操作
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"read3:%d",i);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"read4:%d",i);
        }
    });
}

打印信息:


image.png

從打印信息可以看出讀操作是異步進行的报咳,寫操作是等待當(dāng)前任務(wù)結(jié)束后阻塞任務(wù)隊列獨立進行的侠讯,當(dāng)寫操作結(jié)束后隊列恢復(fù)異步執(zhí)行讀操作,這正是我們需要的效果暑刃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厢漩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岩臣,更是在濱河造成了極大的恐慌溜嗜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件架谎,死亡現(xiàn)場離奇詭異炸宵,居然都是意外死亡,警方通過查閱死者的電腦和手機谷扣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門土全,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捎琐,“玉大人,你說我怎么就攤上這事裹匙∪鸫眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵概页,是天一觀的道長籽御。 經(jīng)常有香客問我,道長惰匙,這世上最難降的妖魔是什么技掏? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮项鬼,結(jié)果婚禮上哑梳,老公的妹妹穿的比我還像新娘。我一直安慰自己秃臣,他們只是感情好涧衙,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布哪工。 她就那樣靜靜地躺著奥此,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雁比。 梳的紋絲不亂的頭發(fā)上稚虎,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音偎捎,去河邊找鬼蠢终。 笑死,一個胖子當(dāng)著我的面吹牛茴她,可吹牛的內(nèi)容都是我干的寻拂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼丈牢,長吁一口氣:“原來是場噩夢啊……” “哼祭钉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起己沛,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤慌核,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后申尼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垮卓,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年师幕,在試婚紗的時候發(fā)現(xiàn)自己被綠了粟按。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钾怔,靈堂內(nèi)的尸體忽然破棺而出碱呼,到底是詐尸還是另有隱情,我是刑警寧澤宗侦,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布愚臀,位于F島的核電站,受9級特大地震影響矾利,放射性物質(zhì)發(fā)生泄漏姑裂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一男旗、第九天 我趴在偏房一處隱蔽的房頂上張望舶斧。 院中可真熱鬧,春花似錦察皇、人聲如沸茴厉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矾缓。三九已至,卻和暖如春稻爬,著一層夾襖步出監(jiān)牢的瞬間嗜闻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工桅锄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留琉雳,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓友瘤,卻偏偏與公主長得像翠肘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辫秧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345