標籤:

GCD的其他方法

6.GCD的其他方法

6.1 GCD的柵欄方法:dispatchbarrierasync

  • 有時我們需要非同步執行兩組操作,而且第一組操作執行完之後,才能開始執行第二組操作。這樣我們就需要一個相當於 柵欄 一樣的一方將兩組非同步執行的操作組給分隔起來,當然這裡的操作組裡可以包含一個或多個任務

//柵欄方法- (void)barrier { dispatch_queue_t queue = dispatch_queue_create("com.baidu", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{// 任務1 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1 --- %@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ // 任務2 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2 --- %@",[NSThread currentThread]); } }); dispatch_barrier_async(queue, ^{ NSLog(@"barrier ====="); [NSThread sleepForTimeInterval:2]; NSLog(@"barrier --- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ // 任務3 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3 --- %@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ // 任務4 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"4 --- %@",[NSThread currentThread]); } });}

輸出結果:

2018-03-29 13:39:21.439678+0800 gcdtest[1881:465694] 2 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

2018-03-29 13:39:21.439682+0800 gcdtest[1881:465693] 1 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:23.442251+0800 gcdtest[1881:465693] 1 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:23.442251+0800 gcdtest[1881:465694] 2 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

2018-03-29 13:39:25.447594+0800 gcdtest[1881:465693] 1 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:25.447596+0800 gcdtest[1881:465694] 2 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

2018-03-29 13:39:25.447788+0800 gcdtest[1881:465693] barrier =====

2018-03-29 13:39:27.450071+0800 gcdtest[1881:465693] barrier --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:29.455425+0800 gcdtest[1881:465693] 3 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:29.455418+0800 gcdtest[1881:465694] 4 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

2018-03-29 13:39:31.456350+0800 gcdtest[1881:465694] 4 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

2018-03-29 13:39:31.456359+0800 gcdtest[1881:465693] 3 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:33.461713+0800 gcdtest[1881:465693] 3 --- <NSThread: 0x60c000262880>{number = 4, name = (null)}

2018-03-29 13:39:33.461724+0800 gcdtest[1881:465694] 4 --- <NSThread: 0x60000007a1c0>{number = 3, name = (null)}

  • 在執行完柵欄前面的操作之後,才執行柵欄操作,最後再執行柵欄後面的操作

6.2 GCD演示執行方法:dispatch_after

我們經常會遇到這樣的需求,在指定時間後執行謀個任務。可以用GCD的dispatch_after函數來實現。

需要注意的是: dispatchafter 函數並不是在指定時間之後才開始執行處理,在是在制定時間後將任務追加到主隊列總。嚴格來說,這個時間並不是絕對準確的,但想要大致延遲任務,dispatchafter 函數是很有效的

- (void)after { NSLog(@"/currentThread --- %@",[NSThread currentThread]); NSLog(@"begin"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"after ---- %@",[NSThread currentThread]); });}

2018-03-29 13:48:45.135239+0800 gcdtest[1938:485264] /currentThread --- <NSThread: 0x60c00007d580>{number = 1, name = main}

2018-03-29 13:48:45.135405+0800 gcdtest[1938:485264] begin

2018-03-29 13:48:48.135729+0800 gcdtest[1938:485264] after ---- <NSThread: 0x60c00007d580>{number = 1, name = main}

  • 可以看出在3秒後,列印出了after

6.4 GCD快速迭代方法:dispatch_apply

  • 通常我們會用for 循環遍歷,但是GCD個我們提供了快速迭代的函數dispatchapply。dispatchapply按照制定的次數,將執行的任務追加到制定的隊列中,並等待全部隊列執行結束。

如果在串列隊列中使用dispatchapply,那麼就喝for循環一樣。

不過dispatchapply在並發隊列驚醒非同步執行:

// 快速迭代方法- (void)apply { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"apply --begin"); dispatch_apply(6, queue, ^(size_t index) { NSLog(@"%zd - -%@",index,[NSThread currentThread]); }); NSLog(@"apply - end") ;}

2018-03-29 13:57:05.766319+0800 gcdtest[1989:500784] apply --begin

2018-03-29 13:57:05.767229+0800 gcdtest[1989:500784] 0 - -<NSThread: 0x60c00006f780>{number = 1, name = main}

2018-03-29 13:57:05.767247+0800 gcdtest[1989:500910] 2 - -<NSThread: 0x60c000278840>{number = 4, name = (null)}

2018-03-29 13:57:05.767250+0800 gcdtest[1989:500909] 1 - -<NSThread: 0x604000078040>{number = 3, name = (null)}

2018-03-29 13:57:05.767255+0800 gcdtest[1989:500913] 3 - -<NSThread: 0x60000007b380>{number = 5, name = (null)}

2018-03-29 13:57:05.767381+0800 gcdtest[1989:500784] 4 - -<NSThread: 0x60c00006f780>{number = 1, name = main}

2018-03-29 13:57:05.767382+0800 gcdtest[1989:500910] 5 - -<NSThread: 0x60c000278840>{number = 4, name = (null)}

2018-03-29 13:57:05.767535+0800 gcdtest[1989:500784] apply - end

  • 因為是咋i並發隊列非同步執行任務,所以各個任務的執行時間長短不定,最後結束順序也不定,但是apply - end 一定是在最後執行。這是因為dispatch_apply函數會等待全部任務執行完畢。

6.5 GCD隊列:dispatch_group

有時候我們會有這樣的需求,分別非同步至此那個2個耗時任務,然後當2個耗時任務都至此那個完畢後再回到主線程執行任務。之後我們可以用的GCD的隊列組。

  • 調用隊列組的 dispatchgroupasync 先把任務放到隊列中,然後將隊列放入隊列組中。或者使用隊列組的 dispatchgroupenter、dispatchgroupleave 組合 來實現

    dispatchgroupasync。
  • 調用隊列組的 dispatchgroupnotify 回到指定線程執行任務。或者使用 dispatchgroupwait 回到當前線程繼續向下執行(會阻塞當前線程)。

6.5.1 dispatchgroupnotify

  • 監聽group中任務的完成狀態,當所有任務都之四那個完成後,追加任務到group中,並執行任務。

//任務組- (void)group { dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務1 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1 --- %@",[NSThread currentThread]); } }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務2 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2 --- %@",[NSThread currentThread]); } }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務3 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3 --- %@",[NSThread currentThread]); } }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的非同步任務1、任務2都執行完畢後,回到主線程執行下邊任務 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"4---%@",[NSThread currentThread]); // 列印當前線程 } NSLog(@"group---end"); });}> 2018-03-29 14:13:37.609427+0800 gcdtest[2101:544510] 2 --- <NSThread: 0x608000072400>{number = 5, name = (null)} 2018-03-29 14:13:37.609427+0800 gcdtest[2101:544512] 3 --- <NSThread: 0x600000072d00>{number = 4, name = (null)} 2018-03-29 14:13:37.609432+0800 gcdtest[2101:544511] 1 --- <NSThread: 0x60c000071f00>{number = 3, name = (null)} 2018-03-29 14:13:39.613370+0800 gcdtest[2101:544512] 3 --- <NSThread: 0x600000072d00>{number = 4, name = (null)} 2018-03-29 14:13:39.613375+0800 gcdtest[2101:544510] 2 --- <NSThread: 0x608000072400>{number = 5, name = (null)} 2018-03-29 14:13:39.613394+0800 gcdtest[2101:544511] 1 --- <NSThread: 0x60c000071f00>{number = 3, name = (null)} 2018-03-29 14:13:41.614858+0800 gcdtest[2101:544512] 3 --- <NSThread: 0x600000072d00>{number = 4, name = (null)} 2018-03-29 14:13:41.614859+0800 gcdtest[2101:544511] 1 --- <NSThread: 0x60c000071f00>{number = 3, name = (null)} 2018-03-29 14:13:41.614870+0800 gcdtest[2101:544510] 2 --- <NSThread: 0x608000072400>{number = 5, name = (null)} 2018-03-29 14:13:43.616189+0800 gcdtest[2101:544430] 4---<NSThread: 0x604000069880>{number = 1, name = main} 2018-03-29 14:13:45.617503+0800 gcdtest[2101:544430] 4---<NSThread: 0x604000069880>{number = 1, name = main} 2018-03-29 14:13:45.617669+0800 gcdtest[2101:544430] group---end>

  • 從dispatchgroupnotify相關代碼運行輸出結果可以看出:

    當所有任務都執行完成之後,才執行dispatchgroupnotify block 中的任務。

6.5.2 dispatchgroupwait

  • 暫停當前線程(阻塞當前線程),等待指定的group中大的任務執行完成後,才會往下繼續執行。

//任務組- (void)groupWait { dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務1 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1 --- %@",[NSThread currentThread]); } }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務2 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2 --- %@",[NSThread currentThread]); } });// 等待上面的任務全部完成後,會往下繼續執行(會阻塞當前線程) dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"group---end");}

2018-03-29 14:18:21.464220+0800 gcdtest[2144:557845] 2 --- <NSThread: 0x604000270b80>{number = 3, name = (null)}

2018-03-29 14:18:21.464205+0800 gcdtest[2144:557842] 1 --- <NSThread: 0x60000007c300>{number = 4, name = (null)}

2018-03-29 14:18:23.464676+0800 gcdtest[2144:557842] 1 --- <NSThread: 0x60000007c300>{number = 4, name = (null)}

2018-03-29 14:18:23.464681+0800 gcdtest[2144:557845] 2 --- <NSThread: 0x604000270b80>{number = 3, name = (null)}

2018-03-29 14:18:25.469804+0800 gcdtest[2144:557842] 1 --- <NSThread: 0x60000007c300>{number = 4, name = (null)}

2018-03-29 14:18:25.469805+0800 gcdtest[2144:557845] 2 --- <NSThread: 0x604000270b80>{number = 3, name = (null)}

2018-03-29 14:18:25.469956+0800 gcdtest[2144:557720] group---end

  • 從dispatchgroupwait相關代碼運行輸出結果可以看出:

    當所有任務執行完成之後,才執行 dispatchgroupwait 之後的操作。但是,使用dispatchgroupwait 會阻塞當前線程。

6.5.3 dispatchgroupenter、dispatchgroupleave

  • dispatchgroupenter 標誌著一個任務追加到group,執行一次,相當於group中未執行完畢任務數 + 1.
  • dispatchgroupleave標誌著一個任務離開了group,執行一次,相當於group中未執行完畢任務數-1
  • 當 group 中未執行完畢任務數為0的時候,才會使dispatchgroupwait解除阻塞,以及執行追加到dispatchgroupnotify中的任務

//任務組 EnterAndLeave- (void)groupEnterAndLeave { dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務1 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1 --- %@",[NSThread currentThread]); } dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 任務2 for (int i = 0; i < 3; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2 --- %@",[NSThread currentThread]); } dispatch_group_leave(group); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的非同步任務1、任務2都執行完畢後,回到主線程執行下邊任務 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"4---%@",[NSThread currentThread]); // 列印當前線程 } NSLog(@"group---end"); });

2018-03-29 14:24:51.671383+0800 gcdtest[2185:577165] 2 --- <NSThread: 0x60800007f740>{number = 3, name = (null)}

2018-03-29 14:24:51.671388+0800 gcdtest[2185:577166] 1 --- <NSThread: 0x60c000271440>{number = 4, name = (null)}

2018-03-29 14:24:53.676362+0800 gcdtest[2185:577165] 2 --- <NSThread: 0x60800007f740>{number = 3, name = (null)}

2018-03-29 14:24:53.676362+0800 gcdtest[2185:577166] 1 --- <NSThread: 0x60c000271440>{number = 4, name = (null)}

2018-03-29 14:24:55.678476+0800 gcdtest[2185:577166] 1 --- <NSThread: 0x60c000271440>{number = 4, name = (null)}

2018-03-29 14:24:55.678476+0800 gcdtest[2185:577165] 2 --- <NSThread: 0x60800007f740>{number = 3, name = (null)}

2018-03-29 14:24:57.679920+0800 gcdtest[2185:577105] 4---<NSThread: 0x60400007e980>{number = 1, name = main}

2018-03-29 14:24:59.681286+0800 gcdtest[2185:577105] 4---<NSThread: 0x60400007e980>{number = 1, name = main}

2018-03-29 14:24:59.681420+0800 gcdtest[2185:577105] group---end

  • 從dispatchgroupenter、dispatchgroupleave相關代碼運行結果中可以看出:當所有任務執行完成之後,才執行 dispatchgroupnotify 中的任務。這裡的dispatchgroupenter、dispatchgroupleave組合,其實等同於dispatchgroupasync。

6.6 GCD 信號量:dispatch_semaphore

GCD 中的信號量是指 Dispatch Semaphore,是持有計數的信號。類似於過高速路收費站的欄杆。可以通過時,打開欄杆,不可以通過時,關閉欄杆。在 Dispatch Semaphore 中,使用計數來完成這個功能,計數為0時等待,不可通過。計數為1或大於1時,計數減1且不等待,可通過。

Dispatch Semaphore 提供了三個函數。

  • dispatchsemaphorecreate:創建一個Semaphore並初始化信號的總量
  • dispatchsemaphoresignal:發送一個信號,讓信號總量加1
  • dispatchsemaphorewait:可以使總信號量減1,當信號總量為0時就會一直等待(阻塞所在線程),否則就可以正常執行。

    注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續執行,然後使用信號量。

    Dispatch Semaphore 在實際開發中主要用於:

保持線程同步,將非同步執行任務轉換為同步執行任務

保證線程安全,為線程加鎖

6.6.1 Dispatch Semaphore 線程同步

下面,我們來利用 Dispatch Semaphore 實現線程同步,將非同步執行任務轉換為同步執行任務。

//信號量//信號量- (void)semaphoreSync { NSLog(@"currentThread---%@",[NSThread currentThread]); // 列印當前線程 NSLog(@"semaphore---begin"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block int number = 1; dispatch_async(queue, ^{ // 追加任務1 [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"1---%@",[NSThread currentThread]); // 列印當前線程 number = 100; dispatch_semaphore_signal(semaphore); }); NSLog(@"first,number = %zd",number); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"semaphore---end,senond ,number = %zd",number);}

2018-03-29 14:34:17.942407+0800 gcdtest[2276:611216] currentThread---<NSThread: 0x60400007e900>{number = 1, name = main}

2018-03-29 14:34:17.942569+0800 gcdtest[2276:611216] semaphore---begin

2018-03-29 14:34:17.942669+0800 gcdtest[2276:611216] first,number = 1

2018-03-29 14:34:19.943110+0800 gcdtest[2276:611293] 1---<NSThread: 0x60c00026ce00>{number = 3, name = (null)}

2018-03-29 14:34:19.943261+0800 gcdtest[2276:611216] semaphore---end,senond ,number = 100

semaphore---end 是在執行完 number = 100; 之後才列印的。而且輸出結果 number 為 100。

這是因為非同步執行不會做任何等待,可以繼續執行任務。非同步執行將任務1追加到隊列之後,不做等待,接著執行dispatchsemaphorewait方法。(從first number 的值為 1 ,能看出來)此時 semaphore == 0,當前線程進入等待狀態。然後,非同步任務1開始執行。任務1執行到dispatchsemaphoresignal之後,總信號量,此時 semaphore == 1,dispatchsemaphorewait方法使總信號量減1,正在被阻塞的線程(主線程)恢復繼續執行。最後列印semaphore---end,number = 100。這樣就實現了線程同步,將非同步執行任務轉換為同步執行任務。

6.6.2 Dispatch Semaphore 線程安全和線程同步(為線程加鎖

線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是線程安全的。

若每個線程中對全局變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全局變數是線程安全的;若有多個線程同時執行寫操作(更改變數),一般都需要考慮線程同步,否則的話就可能影響線程安全。

線程同步:可理解為線程 A 和 線程 B 一塊配合,A 執行到一定程度時要依靠線程 B 的某個結果,於是停下來,示意 B 運行;B 依言執行,再將結果給 A;A 再繼續操作。

舉個簡單例子就是:兩個人在一起聊天。兩個人不能同時說話,避免聽不清(操作衝突)。等一個人說完(一個線程結束操作),另一個再說(另一個線程再開始操作)。

下面,我們模擬火車票售賣的方式,實現 NSThread 線程安全和解決線程同步問題。

場景:總共有50張火車票,有兩個售賣火車票的窗口,一個是北京火車票售賣窗口,另一個是上海火車票售賣窗口。兩個窗口同時售賣火車票,賣完為止。

@property (nonatomic, assign)NSInteger ticketCount; //票數@end@implementation ViewController { dispatch_semaphore_t semaphoreLock;}- (void)viewDidLoad { [super viewDidLoad]; [self initTicketStatusSave];}//信號量 線程安全代碼- (void)initTicketStatusSave { NSLog(@"currentThread---%@",[NSThread currentThread]); // 列印當前線程 NSLog(@"semaphore---begin"); semaphoreLock = dispatch_semaphore_create(1); self.ticketCount = 20; // queue1 代表北京買票窗口 dispatch_queue_t queue1 = dispatch_queue_create("com.baidu1", DISPATCH_QUEUE_SERIAL); // queue1 代表上海買票窗口 dispatch_queue_t queue2 = dispatch_queue_create("com.baidu2", DISPATCH_QUEUE_SERIAL); __weak typeof(self) weakSelf = self; dispatch_async(queue1, ^{ [weakSelf saleTicketSafe]; }); dispatch_async(queue2, ^{ [weakSelf saleTicketSafe]; });}/** * 售賣火車票(線程安全) */- (void)saleTicketSafe { while (1) {// 相當於加鎖 dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER); if (self.ticketCount > 0) { self.ticketCount--; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%zd 窗口:%@", self.ticketCount, [NSThread currentThread]]); [NSThread sleepForTimeInterval:0.2]; } else { NSLog(@"所有火車票均已售完"); // 相當於解鎖 dispatch_semaphore_signal(semaphoreLock); break; } // 相當於解鎖 dispatch_semaphore_signal(semaphoreLock); }}

2018-03-29 14:47:44.856554+0800 gcdtest[2366:653605] currentThread---<NSThread: 0x604000073c40>{number = 1, name = main}

2018-03-29 14:47:44.856706+0800 gcdtest[2366:653605] semaphore---begin

2018-03-29 14:47:44.856964+0800 gcdtest[2366:653648] 剩餘票數:19 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:45.062313+0800 gcdtest[2366:653647] 剩餘票數:18 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:45.265130+0800 gcdtest[2366:653648] 剩餘票數:17 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:45.470492+0800 gcdtest[2366:653647] 剩餘票數:16 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:45.672768+0800 gcdtest[2366:653648] 剩餘票數:15 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:45.875262+0800 gcdtest[2366:653647] 剩餘票數:14 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:46.078640+0800 gcdtest[2366:653648] 剩餘票數:13 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:46.279638+0800 gcdtest[2366:653647] 剩餘票數:12 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:46.485012+0800 gcdtest[2366:653648] 剩餘票數:11 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:46.687461+0800 gcdtest[2366:653647] 剩餘票數:10 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:46.892835+0800 gcdtest[2366:653648] 剩餘票數:9 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:47.097402+0800 gcdtest[2366:653647] 剩餘票數:8 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:47.301449+0800 gcdtest[2366:653648] 剩餘票數:7 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:47.503441+0800 gcdtest[2366:653647] 剩餘票數:6 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:47.703824+0800 gcdtest[2366:653648] 剩餘票數:5 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:47.906701+0800 gcdtest[2366:653647] 剩餘票數:4 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:48.107114+0800 gcdtest[2366:653648] 剩餘票數:3 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:48.310013+0800 gcdtest[2366:653647] 剩餘票數:2 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:48.513316+0800 gcdtest[2366:653648] 剩餘票數:1 窗口:<NSThread: 0x6040002689c0>{number = 3, name = (null)}

2018-03-29 14:47:48.716984+0800 gcdtest[2366:653647] 剩餘票數:0 窗口:<NSThread: 0x6040002684c0>{number = 4, name = (null)}

2018-03-29 14:47:48.921313+0800 gcdtest[2366:653648] 所有火車票均已售完

2018-03-29 14:47:48.921460+0800 gcdtest[2366:653647] 所有火車票均已售完

可以看出,在考慮了線程安全的情況下,使用 dispatch_semaphore 機制之後,得到的票數是正確的,沒有出現混亂的情況。我們也就解決了多個線程同步的問題。

推薦閱讀:

iOS 11又爆出重大漏洞有哪些?
iOS 11 的 Bug 這麼多,庫克本人怎麼看?(附 Bug 解決辦法)
iOS 平台越來越多標榜全手勢操作的應用,有何利弊?如何解決弊端?
在Mac上做開發是否一定要用Retina屏?
iOS開發,IB的效率和Code的效率哪個更高?

TAG:iOS |