每日一問12——多線程之GCD

GCD介紹

Grand Central Dispatch (GCD) 是 Apple 開發(fā)的一個多核編程的解決方法帝洪。

一似舵、主要概括
  • 和operation queue一樣都是基于隊列的并發(fā)編程API,他們通過集中管理大家協(xié)同使用的線程池葱峡。
  • 公開的5個不同隊列:運(yùn)行在主線程中的main queue砚哗,3個不同優(yōu)先級的后臺隊列(High Priority Queue,Default Priority Queue族沃,Low Priority Queue)频祝,以及一個優(yōu)先級更低的后臺隊列Background Priority Queue(用于I/O)
  • 可創(chuàng)建自定義隊列:串行或并列隊列。自定義一般放在Default Priority Queue和Main Queue里脆淹。
  • 操作是在多線程上還是單線程主要是看隊列的類型和執(zhí)行方法常空,并發(fā)隊列異步執(zhí)行才能在多線程,并發(fā)隊列同步執(zhí)行就只會在這個并發(fā)隊列在隊列中被分配的那個線程執(zhí)行盖溺。

二漓糙、基本概念

1.串行與并發(fā)
Serial Queues 串行隊列

串行隊列中的任務(wù)一次執(zhí)行一個,每個任務(wù)只在前一個任務(wù)完成時才開始烘嘱。

gcd-串行隊列

這些任務(wù)的執(zhí)行時機(jī)受到 GCD 的控制昆禽;唯一能確保的事情是 GCD 一次只執(zhí)行一個任務(wù)蝗蛙,并且按照我們添加到隊列的順序來執(zhí)行。

dispatch_queue_t queue1 = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
    for (int i=0; i<100; i++) {
        dispatch_async(queue1, ^{
            NSLog(@"%d",i);
        });
    }

打印順序一定是1.2.3......98.99醉鳖。保證了執(zhí)行順序捡硅。

2.Concurrent Queues 并發(fā)隊列

在并發(fā)隊列中的任務(wù)能得到的保證是它們會按照被添加的順序開始執(zhí)行,但這就是全部的保證了盗棵。任務(wù)可能以任意順序完成壮韭,你不會知道何時開始運(yùn)行下一個任務(wù),或者任意時刻有多少 Block 在運(yùn)行纹因。

gcd-并發(fā)隊列

何時開始一個 Block 完全取決于 GCD 喷屋。如果一個 Block 的執(zhí)行時間與另一個重疊屈张,也是由 GCD 來決定是否將其運(yùn)行在另一個不同的核心上菊霜,如果那個核心可用,否則就用上下文切換的方式來執(zhí)行不同的 Block 宁赤。

dispatch_queue_t queue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i=0; i<100; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d",i);
        });
    }

打印順序1.2.3.4....55.56.53.....20..99惊畏,按照順序添加進(jìn)隊列恶耽,但執(zhí)行結(jié)果并不能保證順序,結(jié)果與GCD的調(diào)度有關(guān)陕截。

2.GCD中的隊列

我們可以通過dispatch_queue_create的方式手動創(chuàng)建隊列驳棱,通過第二個參數(shù)設(shè)置隊列的類型。既上面提到的(串行或并發(fā))隊列农曲。

dispatch_queue_t queue = dispatch_queue_create("queue-label", DISPATCH_QUEUE_CONCURRENT);

除此之外,系統(tǒng)還為我們提供了5種全局隊列

  • Main Dispatch Queue 類型:Serial Dispatch Queue 主線程執(zhí)行
  • Global Dispatch Queue (HIGH) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:高
  • Global Dispatch Queue (DEFAULT) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:默認(rèn)
  • Global Dispatch Queue (LOW) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:低
  • Global Dispatch Queue (BACKGROUND) 類型: Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:后臺
dispatch_queue_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q);
}
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

我們可以發(fā)現(xiàn)驻债,除了Main Dispatch Queue是串行隊列乳规,其他的均為并發(fā)隊列,并且擁有不同的優(yōu)先級合呐。
我們可以通過以下方式獲取這些全局隊列

//獲取主隊列
dispatch_queue_t main_queue = dispatch_get_main_queue();
//通過第一個參數(shù)獲取其他全局隊列暮的。
dispatch_queue_t global_queue = dispatch_get_global_queue(`DISPATCH_QUEUE_PRIORITY_HIGH`, 0);
自定義隊列的優(yōu)先級

我們可以看到,系統(tǒng)提供的全局隊列的不同之處就是隊列的優(yōu)先級不同淌实。不同的優(yōu)先級適合于不同的業(yè)務(wù)場景冻辩。GCD中提供了以下幾種隊列優(yōu)先級:

__QOS_ENUM(qos_class, unsigned int,
    QOS_CLASS_USER_INTERACTIVE
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x21,
    QOS_CLASS_USER_INITIATED
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x19,
    QOS_CLASS_DEFAULT
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x15,
    QOS_CLASS_UTILITY
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x11,
    QOS_CLASS_BACKGROUND
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x09,
    QOS_CLASS_UNSPECIFIED
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x00,
);
  • QOS_CLASS_USER_INTERACTIVE:user interactive等級表示任務(wù)需要被立即執(zhí)行提供好的體驗,用來更新UI拆祈,響應(yīng)事件等恨闪。這個等級最好保持小規(guī)模。
  • QOS_CLASS_USER_INITIATED:user initiated等級表示任務(wù)由UI發(fā)起異步執(zhí)行放坏。適用場景是需要及時結(jié)果同時又可以繼續(xù)交互的時候咙咽。
  • QOS_CLASS_UTILITY:utility等級表示需要長時間運(yùn)行的任務(wù),伴有用戶可見進(jìn)度指示器淤年。經(jīng)常會用來做計算钧敞,I/O蜡豹,網(wǎng)絡(luò),持續(xù)的數(shù)據(jù)填充等任務(wù)溉苛。這個任務(wù)節(jié)能镜廉。
  • QOS_CLASS_BACKGROUND:background等級表示用戶不會察覺的任務(wù),使用它來處理預(yù)加載愚战,或者不需要用戶交互和對時間不敏感的任務(wù)娇唯。

我們可以通過以下方式為自定義的隊列設(shè)置隊列優(yōu)先級

1.使用dispatch_queue_attr_make_with_qos_class自定義一個隊列描述。
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.qosqueue", attr);

dispatch_queue_attr_make_with_qos_class的參數(shù)凤巨。
1.dispatch_queue_attr_t:串行或者并發(fā)
2.dispatch_qos_class_t:填寫上面的調(diào)度類型视乐。
3.relative_priority:qos內(nèi)的相對優(yōu)先級,這個參數(shù)必須比0大比設(shè)置的QOS Class小敢茁。

2.使用dispatch_set_target_queue設(shè)置與目標(biāo)相同類型的隊列佑淀。
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.settargetqueue",NULL); //需要設(shè)置優(yōu)先級的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //參考優(yōu)先級
dispatch_set_target_queue(queue, referQueue); //設(shè)置queue和referQueue的優(yōu)先級一樣

dispatch_set_target_queue:可以設(shè)置優(yōu)先級,也可以設(shè)置隊列層級體系彰檬,比如讓多個串行和并發(fā)隊列在統(tǒng)一一個串行隊列里串行執(zhí)行.

dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t firstQueue = dispatch_queue_create("com.starming.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t secondQueue = dispatch_queue_create("com.starming.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_set_target_queue(firstQueue, serialQueue);
    dispatch_set_target_queue(secondQueue, serialQueue);
    
    dispatch_async(firstQueue, ^{
        NSLog(@"1");
    });
    dispatch_async(secondQueue, ^{
        NSLog(@"2");
    });
    dispatch_async(secondQueue, ^{
        NSLog(@"3");
    });
3.異步和同步

同步和異步的概念主要針對的是我們的線程伸刃。

  • 同步是指在當(dāng)前線程下執(zhí)行隊列中的任務(wù),并且同步任務(wù)執(zhí)行完畢后才執(zhí)行后面的任務(wù)逢倍。
  • 異步是指在子線程(非當(dāng)前線程)下執(zhí)行隊列中的任務(wù)捧颅。執(zhí)行任務(wù)的順序與當(dāng)前線程中的任務(wù)無關(guān)。
    在GCD中较雕,異步執(zhí)行使用的是dispatch_async碉哑,同步執(zhí)行使用dispatch_sync
dispatch_sync同步執(zhí)行
- (void)viewDidLoad
{
  [super viewDidLoad];
 
  dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 
      NSLog(@"First Log");
 
  });
 
  NSLog(@"Second Log");
}
同步執(zhí)行解釋

下面是圖中幾個步驟的說明:

1.主隊列一路按順序執(zhí)行任務(wù)——接著是一個實(shí)例化 UIViewController 的任務(wù)亮蒋,其中包含了 viewDidLoad 扣典。
2.viewDidLoad 在主線程執(zhí)行。
3.主線程目前在 viewDidLoad 內(nèi)慎玖,正要到達(dá) dispatch_sync 贮尖。
4.dispatch_sync Block 被添加到一個全局隊列中,將在稍后執(zhí)行趁怔。進(jìn)程將在主線程掛起直到該 Block 完成湿硝。同時,全局隊列并發(fā)處理任務(wù)润努;要記得 Block 在全局隊列中將按照 FIFO 順序出列关斜,但可以并發(fā)執(zhí)行。
5.全局隊列處理 dispatch_sync Block 加入之前已經(jīng)出現(xiàn)在隊列中的任務(wù)任连。
6.終于蚤吹,輪到 dispatch_sync Block 。
7.這個 Block 完成,因此主線程上的任務(wù)可以恢復(fù)裁着。
8.viewDidLoad 方法完成繁涂,主隊列繼續(xù)處理其他任務(wù)。

小結(jié):dispatch_sync 添加任務(wù)到一個隊列并等待直到任務(wù)完成二驰。dispatch_async 做類似的事情扔罪,但不同之處是它不會等待任務(wù)的完成,而是立即繼續(xù)“調(diào)用線程”的其它任務(wù)桶雀。

使用dispatch_async矿酵。

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 
      NSLog(@"First Log");
 
  });
 
  NSLog(@"Second Log");
}
異步執(zhí)行解釋

1.主隊列一路按順序執(zhí)行任務(wù)——接著是一個實(shí)例化 UIViewController 的任務(wù),其中包含了 viewDidLoad 矗积。
2.viewDidLoad 在主線程執(zhí)行全肮。
3.主線程目前在 viewDidLoad 內(nèi),正要到達(dá) dispatch_async 棘捣。
4.dispatch_async Block 被添加到一個全局隊列中辜腺,將在稍后執(zhí)行。
5.viewDidLoad 在添加 dispatch_async 到全局隊列后繼續(xù)進(jìn)行乍恐,主線程把注意力轉(zhuǎn)向剩下的任務(wù)评疗。同時,全局隊列并發(fā)地處理它未完成地任務(wù)茵烈。記住 Block 在全局隊列中將按照 FIFO 順序出列百匆,但可以并發(fā)執(zhí)行。
6.添加到 dispatch_async 的代碼塊開始執(zhí)行呜投。
7.dispatch_async Block 完成加匈,兩個 NSLog 語句將它們的輸出放在控制臺上。

小結(jié):在這個特定的實(shí)例中仑荐,第二個 NSLog 語句執(zhí)行矩动,跟著是第一個 NSLog 語句。并不總是這樣——著取決于給定時刻硬件正在做的事情释漆,而且你無法控制或知曉哪個語句會先執(zhí)行±河“第一個” NSLog 在某些調(diào)用情況下會第一個執(zhí)行男图。

三、GCD的使用

1.用 dispatch_async 處理后臺任務(wù)

通過異步執(zhí)行下載任務(wù)可以避免界面被一些耗時操作卡死甜橱,例如讀取網(wǎng)絡(luò)數(shù)據(jù)逊笆,大數(shù)據(jù)IO,還有大量數(shù)據(jù)的數(shù)據(jù)庫讀寫岂傲,這時需要在另一個線程中處理难裆,然后通知主線程更新界面。我們常常會經(jīng)常這樣使用GCD

//代碼框架
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     // 耗時的操作
     dispatch_async(dispatch_get_main_queue(), ^{
          // 更新界面
     });
});

當(dāng)我們需要下載一張圖片褂痰,并顯示到界面時缩歪,我們可以這樣處理。防止界面因下載圖片被卡死谍憔。

//下載圖片的示例
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
     NSData * data = [[NSData alloc]initWithContentsOfURL:url];
     UIImage *image = [[UIImage alloc]initWithData:data];
     if (data != nil) {
          dispatch_async(dispatch_get_main_queue(), ^{
               self.imageView.image = image;
          });
     }
});

運(yùn)行邏輯:

  • 創(chuàng)建一個并發(fā)隊列并使用dispatch_async異步執(zhí)行這個隊列里面的任務(wù)
  • 在子線程中下載圖片數(shù)據(jù)匪蝙,生成對應(yīng)的UIimage對象。
  • 異步切換到主線程隊列习贫,修改UI逛球。
2.dispatch_after延后執(zhí)行

dispatch_after只是延時提交block,不是延時立刻執(zhí)行

NSLog(@"1");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"2");
});
NSLog(@"3");

打印順序是1苫昌,3颤绕,2。dispatch_after并不會阻塞當(dāng)前線程蜡歹,只是延遲提交block屋厘,在當(dāng)前線程繼續(xù)執(zhí)行。
其中的dispatch time參數(shù)的函數(shù)原型如下:

dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );

第一個參數(shù)為DISPATCH_TIME_NOW表示當(dāng)前月而。第二個參數(shù)的delta表示納秒汗洒,一秒對應(yīng)的納秒為1000000000,系統(tǒng)提供了一些宏來簡化

 #define NSEC_PER_SEC 1000000000ull //每秒有多少納秒
 #define USEC_PER_SEC 1000000ull    //每秒有多少毫秒
 #define NSEC_PER_USEC 1000ull      //每毫秒有多少納秒
3.dispatch_once創(chuàng)建一個線程安全的單例

先說說不安全的創(chuàng)建情況

+ (instancetype)sharedManager {
    static SingleClass *manager = nil;
    if(!manager) {
        manager = [SingleClass new];
    }
    return manager;
}

當(dāng)我們在不同線程多次調(diào)用改方法時父款,很有可能發(fā)生A線程進(jìn)入if代碼塊后溢谤,系統(tǒng)切換上下文世杀,B線程也進(jìn)入代碼塊造成對象被初始化2次的問題。于是我們就需要保證單例的線程安全所刀。

static SingleClass *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [SingleClass new];
    });
    return manager;

其實(shí)就是使用dispatch_once 取代if 條件判斷,dispatch_once() 以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次砌函。試圖訪問臨界區(qū)(即傳遞給 dispatch_once 的代碼)的不同的線程會在臨界區(qū)已有一個線程的情況下被阻塞垦沉,直到臨界區(qū)完成為止摧玫。

4.dispatch_apply進(jìn)行快速迭代
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(999, queue, ^(size_t i) {
        NSLog(@"%d",i);
    });
    NSLog(@"end");

輸出結(jié)果1,2....998,end
說明這個方法是會阻塞當(dāng)前線程的闸婴。并且可以循環(huán)執(zhí)行并發(fā)操作降狠。它的優(yōu)點(diǎn)在于執(zhí)行并發(fā)操作時GCD會進(jìn)行線程管理。

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    if (explode) {
        //有問題的情況,可能會死鎖
        for (int i = 0; i < 999 ; i++) {
            dispatch_async(concurrentQueue, ^{
                NSLog(@"wrong %d",i);
                //do something hard
            });
        }
    } else {
        //會優(yōu)化很多,能夠利用GCD管理
        dispatch_apply(999, concurrentQueue, ^(size_t i){
            NSLog(@"correct %zu",i);
            //do something hard
        });
    }
5.dispatch_barrier_async解決多線程對同一資源進(jìn)行讀寫的沖突問題
__block NSString *str = @"1";
    dispatch_queue_t dataQueue = dispatch_queue_create("com.starming.gcddemo.dataqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(dataQueue, ^{
        NSLog(@"%@",str);
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"%@",str);
    });
    //等待前面的都完成,在執(zhí)行barrier后面的
    dispatch_barrier_async(dataQueue, ^{
        str = @"2";
        NSLog(@"%@",str);
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"%@",str);
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"%@",str);
    });

dispatch_barrier_async保證了同一隊列中,先提交的任務(wù)執(zhí)行完畢后才執(zhí)行自己提交的block辆苔。并且提交的block執(zhí)行完畢后荐吵,隊列會恢復(fù)成以前的樣子。

dispatch_barrier_async只在自己創(chuàng)建的隊列上有這種作用遥倦,在全局并發(fā)隊列和串行隊列上,效果和dispatch_sync一樣

6. dispatch_group線程組

dispatch groups是專門用來監(jiān)視多個異步任務(wù)堡称。dispatch_group_t實(shí)例用來追蹤不同隊列中的不同任務(wù)胎撤。
最常見的例子就是巫俺,多個異步并發(fā)任務(wù)執(zhí)行完畢后再執(zhí)行另外的操作。比如上傳一組圖片,或者下載多個文件赶撰。希望在全部完成時給用戶一個提示瘤载。

當(dāng)group里所有事件都完成GCD API有兩種方式發(fā)送通知墨技,

  • 第一種是dispatch_group_wait,會阻塞當(dāng)前進(jìn)程,等所有任務(wù)都完成或等待超時。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    //在group中添加隊列的block
    dispatch_group_async(group, concurrentQueue, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"1");
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"2");
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"go on");
  • 第二種方法是使用dispatch_group_notify,異步執(zhí)行閉包学搜,不會阻塞。
dispatch_group_t serviceGroup = dispatch_group_create();
    
    // 開始第一個請求
    // 進(jìn)入組
    dispatch_group_enter(serviceGroup);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"1");
        dispatch_group_leave(serviceGroup);
    });
    
    // 開始第二個請求
    // 先進(jìn)入組
    dispatch_group_enter(serviceGroup);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_group_leave(serviceGroup);
    });
    
    // 當(dāng)小組里的任務(wù)都清空以后,通知主線程完成了所有任務(wù)
    dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
        // Assess any errors
        NSLog(@"finish");
    });
NSLog(@"go on");

此處會先打印go on。

7.使用信號量Dispatch Semaphore

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

  • dispatch_semaphore_create 初始化一個信號量毅桃,并設(shè)置它最大訪問數(shù)量读宙。
  • dispatch_semaphore_wait 向某個信號量對象發(fā)送等待信號掖棉,信號量-1察纯,設(shè)置該次等待的時間,使用DISPATCH_TIME_FOREVER可以表示永久等待针肥。
  • dispatch_semaphore_signal 向某個信號量發(fā)送釋放信號饼记,信號量+1。
    當(dāng)信號量計數(shù)沒有超過最大訪問數(shù)量時慰枕,資源區(qū)可以被多個線程同時訪問具则。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    __block NSString *strTest = @"test";
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--1-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--2-", strTest);
        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);
        NSLog(@"--%@--4-", strTest);
        dispatch_semaphore_signal(semaphore);
    });

打印結(jié)果:1掘猿,2唧龄,3眼姐,4.
解析:并發(fā)隊列只能保證按提交順序執(zhí)行律想,本來是不能保證執(zhí)行結(jié)果順序的,但在這里我們使用了dispatch_semaphore_t限制了資源的訪問,由于最大只允許1個資源訪問川梅,所以這段代碼一定是按順序執(zhí)行的。

8.Dispatch Source監(jiān)聽進(jìn)程事件

建議閱讀iOS多線程——Dispatch Source
我試了一下里面的幾個例子苦掘,對Dispatch Source有了一定的理解慎颗,但沒有在開發(fā)中實(shí)際使用,等后面有實(shí)際經(jīng)驗再記錄分享营搅。

細(xì)說GCD(Grand Central Dispatch)如何用
iOS多線程GCD簡介(一)
iOS多線程GCD簡介(二)
GCD 深入理解:第一部分

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠕嫁,死亡現(xiàn)場離奇詭異锨天,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)剃毒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門绍绘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迟赃,你說我怎么就攤上這事〕д颍” “怎么了纤壁?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捺信。 經(jīng)常有香客問我酌媒,道長,這世上最難降的妖魔是什么迄靠? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任秒咨,我火速辦了婚禮,結(jié)果婚禮上掌挚,老公的妹妹穿的比我還像新娘雨席。我一直安慰自己,他們只是感情好吠式,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布陡厘。 她就那樣靜靜地躺著,像睡著了一般特占。 火紅的嫁衣襯著肌膚如雪糙置。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天是目,我揣著相機(jī)與錄音谤饭,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛揉抵,可吹牛的內(nèi)容都是我干的亡容。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼功舀,長吁一口氣:“原來是場噩夢啊……” “哼萍倡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辟汰,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤列敲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帖汞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戴而,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年翩蘸,在試婚紗的時候發(fā)現(xiàn)自己被綠了所意。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡催首,死狀恐怖扶踊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郎任,我是刑警寧澤秧耗,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站舶治,受9級特大地震影響分井,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霉猛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一尺锚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惜浅,春花似錦瘫辩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吹散,卻和暖如春弧械,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背空民。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工刃唐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羞迷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓画饥,卻偏偏與公主長得像衔瓮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抖甘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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