Grand Central Dispatch
純C語言月培,提供了非常多強大的函數(shù)
線程相關知識以及GCD概念介紹不是本篇重點這里不做過多涉及
(一)OC篇
只想看swift的可以直接跳過,看下面的swift篇
隊列的創(chuàng)建
- dispatch_queue_create(param1,param2)
/**
創(chuàng)建一個隊列
@param "identifier" 隊列的唯一標識
@param DISPATCH_QUEUE_SERIAL 串行隊列還是并行隊列
@return 返回實例對象(姑且認為它是個對象)
*/
dispatch_queue_t queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_SERIAL);
- param1需要傳入一個c語言類型字符串,作用是用來標記當前queue,可以傳空.可以通過dispatch_queue_get_label來獲取queue的標記.
const *char s = dispatch_queue_get_label(queue);
-
param2傳入Dispatch Queue Types,來決定queue的類型.
串行隊列
DISPATCH_QUEUE_SERIAL
并行隊列
DISPATCH_QUEUE_CONCURRENT
主隊列 dispatch_get_main_queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局隊列 dispatch_get_global_queue(param1, param2)
會獲取一個全局隊列门岔,我們姑且理解為系統(tǒng)為我們開啟的一些全局線程烹棉。我們用priority指定隊列的優(yōu)先級豌拙,而flag作為保留字段備用(一般為0)。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
- param1 傳入隊列優(yōu)先級.
- param2 保留字段,目前沒什么用,直接傳0.
隊列中任務的執(zhí)行:
- 串行(Serial):讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后镶苞,再執(zhí)行下一個任務)
- 并發(fā)(Concurrent):可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效肝断。
- 同步(Synchronous):在當前線程中執(zhí)行任務,不具備開啟新線程的能力
- 異步(Asynchronous):在新的線程中執(zhí)行任務丈钙,具備開啟新線程的能力
- 串行隊列,同步執(zhí)行
- (void)sync_serial {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("com.test.id", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
控制臺日志如下:
2018-01-23 15:53:08.274847+0800 Thread[74746:3368362] mian_start
2018-01-23 15:53:08.275235+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:11.276825+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:11.277165+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:14.277633+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:14.277845+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:17.278809+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:17.279073+0800 Thread[74746:3368362] mian_end
總結:不重新開辟線程,線程中的任務按順序執(zhí)行.
- 并行隊列,同步執(zhí)行
- (void)sync_Concurrent {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("222", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
打印日志如下:
2018-01-29 11:53:22.464506+0800 Thread[60281:937006] mian_start
2018-01-29 11:53:22.464832+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:25.465294+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:25.465514+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:28.466237+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:28.466548+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:31.467731+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:31.467904+0800 Thread[60281:937006] mian_end
總結:不開辟新線程,任務按順序執(zhí)行.
- 串行隊列,異步執(zhí)行
- (void)async_serial {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("333", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
日志如下:
2018-01-29 11:56:52.776368+0800 Thread[60281:937006] mian_start
2018-01-29 11:56:52.776701+0800 Thread[60281:937006] mian_end
2018-01-29 11:56:52.776777+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:55.779400+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:55.779647+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:58.784670+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:58.784963+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:57:01.786699+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
總結:只會另外開辟一個線程,線程中的任務按順序執(zhí)行.而且新開辟的線程不會阻塞當前線程.兩個線程中的代碼同時執(zhí)行.
- 并行隊列,異步執(zhí)行
- (void)async_concurrent {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("333", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
日志如下:
2018-01-29 13:07:10.507523+0800 Thread[60281:937006] mian_start
2018-01-29 13:07:10.507810+0800 Thread[60281:937006] mian_end
2018-01-29 13:07:10.507901+0800 Thread[60281:1109308] tread_begin ,<NSThread: 0x6040004654c0>{number = 7, name = (null)}
2018-01-29 13:07:10.507903+0800 Thread[60281:951997] tread_begin ,<NSThread: 0x600000476d80>{number = 8, name = (null)}
2018-01-29 13:07:10.507926+0800 Thread[60281:1109326] tread_begin ,<NSThread: 0x60400046c740>{number = 9, name = (null)}
2018-01-29 13:07:13.512128+0800 Thread[60281:1109308] tread_end ,<NSThread: 0x6040004654c0>{number = 7, name = (null)}
2018-01-29 13:07:13.512173+0800 Thread[60281:951997] tread_end ,<NSThread: 0x600000476d80>{number = 8, name = (null)}
2018-01-29 13:07:13.512221+0800 Thread[60281:1109326] tread_end ,<NSThread: 0x60400046c740>{number = 9, name = (null)}
總結:會開啟多個線程,.新開辟的線程不會相互阻塞.多個線程中的任務同時執(zhí)行.
調度組 dispatch_group_t
項目中,我們有時候可能會遇到這樣的場景.需要開啟多個線程并發(fā)執(zhí)行任務,最后需要在所有任務執(zhí)行完成后再做操作.這里我們就可以使用GCD中的調度組來實現(xiàn):
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"調度組開始執(zhí)行!!!");
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread1 -> %d", i);
}
});
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread2 -> %d", i);
}
});
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread3 -> %d", i);
}
});
dispatch_group_notify(group, queue, ^{
NSLog(@"所有循環(huán)執(zhí)行完畢!!!");
});
打印日志如下:
2018-02-01 17:33:33.687543+0800 Thread[63088:367476] 調度組開始執(zhí)行!!!
2018-02-01 17:33:33.687879+0800 Thread[63088:367562] thread1 -> 0
2018-02-01 17:33:33.687882+0800 Thread[63088:367553] thread2 -> 0
2018-02-01 17:33:33.687888+0800 Thread[63088:367558] thread3 -> 0
2018-02-01 17:33:33.688551+0800 Thread[63088:367562] thread1 -> 1
2018-02-01 17:33:33.688569+0800 Thread[63088:367553] thread2 -> 1
2018-02-01 17:33:33.688671+0800 Thread[63088:367558] thread3 -> 1
2018-02-01 17:33:33.688963+0800 Thread[63088:367553] thread2 -> 2
2018-02-01 17:33:33.688982+0800 Thread[63088:367562] thread1 -> 2
2018-02-01 17:33:33.689465+0800 Thread[63088:367558] thread3 -> 2
2018-02-01 17:33:33.689706+0800 Thread[63088:367553] thread2 -> 3
2018-02-01 17:33:33.690041+0800 Thread[63088:367562] thread1 -> 3
2018-02-01 17:33:33.690279+0800 Thread[63088:367558] thread3 -> 3
2018-02-01 17:33:33.691043+0800 Thread[63088:367553] thread2 -> 4
2018-02-01 17:33:33.691211+0800 Thread[63088:367562] thread1 -> 4
2018-02-01 17:33:33.691389+0800 Thread[63088:367558] thread3 -> 4
2018-02-01 17:33:33.691583+0800 Thread[63088:367553] thread2 -> 5
2018-02-01 17:33:33.691826+0800 Thread[63088:367562] thread1 -> 5
2018-02-01 17:33:33.692019+0800 Thread[63088:367558] thread3 -> 5
2018-02-01 17:33:33.692796+0800 Thread[63088:367558] 所有循環(huán)執(zhí)行完畢!!!
總結:所有任務執(zhí)行完成后的操作在 dispatch_group_notify 中寫代碼即可.
- 但是這里有一個問題:隊列中的任務代碼是一段for循環(huán),for循環(huán)是同步執(zhí)行的.如果代碼塊中的任務是異步執(zhí)行的,還會是現(xiàn)在的效果嗎?
- 比如: 我要發(fā)起3個異步的網(wǎng)絡請求,需要等到3個請求都有響應后再做下一步操作.此時我們仍然使用上面的代碼是否能實現(xiàn)呢?
- 這里我用延時操作來模擬異步網(wǎng)絡請求延時效果.
- (void)group_asynBlock {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"group 開始執(zhí)行 !!!");
dispatch_group_async(group, queue, ^{
NSLog(@"block1 begin !!!");
[self request:1];
NSLog(@"block1 end !!!");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block2 begin !!!");
[self request:2];
NSLog(@"block2 end !!!");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block3 begin !!!");
[self request:3];
NSLog(@"block3 end !!!");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"group 結束 !!!");
});
}
- (void)request:(NSInteger)count {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"請求%ld 開始 !!!", count);
[NSThread sleepForTimeInterval:3];
NSLog(@"請求%ld 結束 !!!", count);
});
}
下面我可們可以來猜測一下日志會是怎樣的.
我們所期望的是這樣的
group 開始執(zhí)行 !!!
block1 begin !!!
block2 begin !!!
block3 begin !!!
block1 end !!!
block2 end !!!
block3 end !!!
請求2 開始 !!!
請求1 開始 !!!
請求3 開始 !!!
請求2 結束 !!!
請求1 結束 !!!
請求3 結束 !!!
group 結束 !!!
但是實際日志是這樣的:
2018-02-01 17:55:42.008339+0800 Thread[67540:402087] group 開始執(zhí)行 !!!
2018-02-01 17:55:42.008609+0800 Thread[67540:402136] block1 begin !!!
2018-02-01 17:55:42.008619+0800 Thread[67540:402132] block2 begin !!!
2018-02-01 17:55:42.008631+0800 Thread[67540:402133] block3 begin !!!
2018-02-01 17:55:42.009244+0800 Thread[67540:402136] block1 end !!!
2018-02-01 17:55:42.009277+0800 Thread[67540:402132] block2 end !!!
2018-02-01 17:55:42.009280+0800 Thread[67540:402134] 請求1 開始 !!!
2018-02-01 17:55:42.009309+0800 Thread[67540:402135] 請求2 開始 !!!
2018-02-01 17:55:42.009444+0800 Thread[67540:402133] block3 end !!!
2018-02-01 17:55:42.009456+0800 Thread[67540:402136] 請求3 開始 !!!
2018-02-01 17:55:42.010551+0800 Thread[67540:402133] group 結束 !!!
2018-02-01 17:55:45.013680+0800 Thread[67540:402135] 請求2 結束 !!!
2018-02-01 17:55:45.013673+0800 Thread[67540:402134] 請求1 結束 !!!
2018-02-01 17:55:45.014906+0800 Thread[67540:402136] 請求3 結束 !!!
可以看到,group執(zhí)行結束代碼的時機是不確定的,這里請求還沒結束,group notify你面的代碼卻已經(jīng)執(zhí)行了.
下面來說下group notify 什么時候會調用:
當所有的group中的代碼塊中的代碼從上至下執(zhí)行完成后就會調用notify.所以如果代碼塊中都是同步執(zhí)行的還好,如果出現(xiàn)了異步執(zhí)行的情況,因為即使是異步,當前代碼塊中的代碼仍然會往下繼續(xù)執(zhí)行.所以無論異步執(zhí)行的任務是否完成并不能起到?jīng)Q定作用.于是就出現(xiàn)了上面的問題.
那我們如何來解決group中存在異步執(zhí)行代碼的情況呢.
此時需用到 dispatch_group_enter() 和 dispatch_group_leave()
上面代碼需要做如下修改
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"group 開始執(zhí)行 !!!");
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block1 begin !!!");
[self request:1 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block1 end !!!");
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block2 begin !!!");
[self request:2 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block2 end !!!");
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block3 begin !!!");
[self request:3 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block3 end !!!");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"group 結束 !!!");
});
- (void)request:(NSInteger)count block:(void(^)(void))complete{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"請求%ld 開始 !!!", count);
[NSThread sleepForTimeInterval:3];
NSLog(@"請求%ld 結束 !!!", count);
if(complete)
complete();
});
}
日志如下:
2018-04-04 10:57:03.655066+0800 Thread[13280:128963] group 開始執(zhí)行 !!!
2018-04-04 10:57:03.655381+0800 Thread[13280:129076] block1 begin !!!
2018-04-04 10:57:03.655385+0800 Thread[13280:129085] block2 begin !!!
2018-04-04 10:57:03.655445+0800 Thread[13280:129078] block3 begin !!!
2018-04-04 10:57:03.657045+0800 Thread[13280:129076] block1 end !!!
2018-04-04 10:57:03.657073+0800 Thread[13280:129079] 請求1 開始 !!!
2018-04-04 10:57:03.657078+0800 Thread[13280:129085] block2 end !!!
2018-04-04 10:57:03.657113+0800 Thread[13280:129078] block3 end !!!
2018-04-04 10:57:03.657184+0800 Thread[13280:129076] 請求2 開始 !!!
2018-04-04 10:57:03.657296+0800 Thread[13280:129085] 請求3 開始 !!!
2018-04-04 10:57:06.659375+0800 Thread[13280:129085] 請求3 結束 !!!
2018-04-04 10:57:06.659375+0800 Thread[13280:129076] 請求2 結束 !!!
2018-04-04 10:57:06.659403+0800 Thread[13280:129079] 請求1 結束 !!!
2018-04-04 10:57:06.659753+0800 Thread[13280:129076] group 結束 !!!
如上,結果和我們所期望的一致.在每一個異步任務開始之前,我們需要調用dispatch_group_enter(group)來防止block塊內代碼執(zhí)行完畢后,退出group.然后在異步請求響應后的回調里調用dispatch_group_leave(group)使其正常離開group就ok了.
dispatch_group_enter()和dispatch_group_leave()需要成對出現(xiàn)
GCD 柵欄方法:dispatch_barrier_async
- 一般使用場景,我們開啟了多個異步任務.但是需要將其分組,并控制每組的執(zhí)行順序的時候可以使用該api
- (void)barrier {
NSInteger count = 3;
dispatch_queue_t queue = dispatch_queue_create("queue_label", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread1 --> %d", i);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread2 --> %d", i);
}
});
dispatch_barrier_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"barrier --> %d", i);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread3 --> %d", i);
}
});
}
日志如下:
2018-04-04 14:38:29.095615+0800 Thread[15169:299036] Thread2 --> 0
2018-04-04 14:38:29.095611+0800 Thread[15169:298974] Thread1 --> 0
2018-04-04 14:38:29.095876+0800 Thread[15169:299036] Thread2 --> 1
2018-04-04 14:38:29.095876+0800 Thread[15169:298974] Thread1 --> 1
2018-04-04 14:38:29.096505+0800 Thread[15169:299036] Thread2 --> 2
2018-04-04 14:38:29.096861+0800 Thread[15169:298974] Thread1 --> 2
2018-04-04 14:38:29.097109+0800 Thread[15169:298974] barrier --> 0
2018-04-04 14:38:29.097212+0800 Thread[15169:298974] barrier --> 1
2018-04-04 14:38:29.097304+0800 Thread[15169:298974] barrier --> 2
2018-04-04 14:38:29.097414+0800 Thread[15169:298974] Thread3 --> 0
2018-04-04 14:38:29.097504+0800 Thread[15169:298974] Thread3 --> 1
2018-04-04 14:38:29.097908+0800 Thread[15169:298974] Thread3 --> 2
可以看出barrier之前的任務會先執(zhí)行,barrier之后的代碼會后執(zhí)行.barrier顧名思義起到了一個分割的作用.
GCD掛起和恢復
- dispatch_suspend()掛起某一個隊列,并不是真正意義上的停止,已經(jīng)開始執(zhí)行的代碼塊是不會停止的.只會停止執(zhí)行還未執(zhí)行的block代碼塊
- dispatch_resume()恢復隊列任務的執(zhí)行.和suspend成對出現(xiàn).
- (void)suspend {
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一個block非驮,延時5秒打印。
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"五秒后打印雏赦,隊列掛起時已經(jīng)開始執(zhí)行劫笙,");
});
//提交第二個block,也是延時5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"隊列掛起時未執(zhí)行星岗,需恢復隊列后在執(zhí)行");
});
//延時一秒
NSLog(@"立刻打印~~~~~~~");
[NSThread sleepForTimeInterval:1];
//掛起隊列
NSLog(@"一秒后打印填大,隊列掛起");
dispatch_suspend(queue);
//延時10秒
[NSThread sleepForTimeInterval:11];
NSLog(@"十秒后打印,開啟隊列");
//恢復隊列
dispatch_resume(queue);
}
日志如下:
2018-04-04 16:46:12.574933+0800 Thread[17344:474988] 立刻打印~~~~~~~
2018-04-04 16:46:13.576395+0800 Thread[17344:474988] 一秒后打印俏橘,隊列掛起
2018-04-04 16:46:17.576697+0800 Thread[17344:475108] 五秒后打印允华,隊列掛起時已經(jīng)開始執(zhí)行,
2018-04-04 16:46:24.577738+0800 Thread[17344:474988] 十秒后打印寥掐,開啟隊列
2018-04-04 16:46:29.583528+0800 Thread[17344:475104] 隊列掛起時未執(zhí)行靴寂,需恢復隊列后在執(zhí)行
GCD 快速迭代方法:dispatch_apply
類似for循環(huán),可以用來遍歷
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zd", index);
});
NSLog(@"遍歷結束 !!!");
}
日志如下:
2018-04-04 17:11:42.159210+0800 Thread[18256:512357] 1
2018-04-04 17:11:42.159218+0800 Thread[18256:512356] 2
2018-04-04 17:11:42.159241+0800 Thread[18256:512358] 3
2018-04-04 17:11:42.159210+0800 Thread[18256:512257] 0
2018-04-04 17:11:42.159453+0800 Thread[18256:512357] 4
2018-04-04 17:11:42.159453+0800 Thread[18256:512358] 5
2018-04-04 17:11:42.159454+0800 Thread[18256:512356] 6
2018-04-04 17:11:42.159465+0800 Thread[18256:512257] 7
2018-04-04 17:11:42.159568+0800 Thread[18256:512357] 8
2018-04-04 17:11:42.159741+0800 Thread[18256:512358] 9
2018-04-04 17:11:42.160506+0800 Thread[18256:512257] 遍歷結束 !!!
- 這里不是有序遍歷是因為,我們上面創(chuàng)建的是一個全局隊列,是異步執(zhí)行的.所以順序是隨機的.如果想要順序遍歷,可以創(chuàng)建一個串行隊列.