上一节,主要介绍了GCD的基本的概念,这节将用代码深入详细介绍GCD的使用。
一 使用介绍
GCD的使用主要分为三步:创建代码块;选择或创建合适的分发队列;(同步、异步方式)向分发队列提交任务
1.创建代码块
提交到GCD中的代码块,是没有返回值的,他们都是dispatch_block_t类型的代码块,dispatch_queue_t 的定义如下:
typedef void (^dispatch_block_t)( void)
意味着加入 dispatch_queue 中的 block 必须是无参数也无返回值的。除了用代码块封装task外,我们也可以使用C语言函数来编写,然后将其提交到分发队列中,但是一般情况下建议使用代码块,这样可以充分利用代码块的“闭包”性质。
2.选择或者创建合适的分发队列
对于主队列和全局队列系统会默认创建,而且全局队列会有三个不同优先级(高、中、默认),向主线程中提交的代码块会在主线程中执行。
3.(同步、异步方式)提交任务
可以用异步或同步的方式来提交一个代码块,具体选择什么样的方式可以根据情况而定。除此之外,也可以选择延迟方式提交。
二 队列的使用
1.主队列
与UI相关的任务应该必须在主线程中来执行,使用dispatch_get_main_queue函数来取得主分发队列的句柄。可以以两种方式向主队列中来提交任务,这两种方式都是异步的,即不管该任务是否执行完毕,都不会阻塞下面任务的执行。
dispatch_async 执行一个代码块
dispatch_async_f 执行一个c函数(不常用)
注意:在主线程中不能以(当前提交任务所在的函数在主线程中)dispathch_sync同步的方式提交任务,因为如果此任务比较耗时的话,会阻塞后面的代码(主线程),导致无法响应用户的请求。所有在主线程中向主队列中提前的任务必须是异步的。
void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
dispatch_async 有两个参数:
1.分发队列的句柄
2.待执行的代码块,没有返回值和参数
a.创建代码块
void (^TaskOne)(void) = ^(void)
{
NSLog(@"Current thread = %@", [NSThread currentThread]);
NSLog(@"Main thread = %@", [NSThread mainThread]);
[[[UIAlertView alloc] initWithTitle:@"GCD"
message:@"Great Center Dispatcher"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil] show];
};
b.取得分发队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
c.提交任务
dispatch_async(mainQueue, TaskOne);
也可以一步完成:
dispatch_async( dispatch_get_main_queue(), ^(void)
{
NSLog(@"Current thread = %@", [NSThread currentThread]);
NSLog(@"Main thread = %@", [NSThread mainThread]);
[[[UIAlertView alloc] initWithTitle:@"GCD"
message:@"Great Center Dispatcher"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil] show];
});
输出结果:
2013-11-08 15:09:27.268 GCDDemo[8567:70b] Current thread = <NSThread: 0x8a166c0>{name = (null), num = 1}
2013-11-08 15:09:27.270 GCDDemo[8567:70b] Main thread = <NSThread: 0x8a166c0>{name = (null), num = 1}
上面定义的代码块的作用是用来打印当前的线程号和主线程号,通过输出发现他们都为1,说明代码块是在主线程中执行的。
2.全局队列
下载并显示图片或大量的后台计算操作,可以使用GCD的全局同步队列。可以同步或异步的方式提交我们的任务。同步提交执行并不是意味着程序会阻塞一直到你的代码执行完毕,它只是意味着,同步队列会一直等待直达你的任务执行完毕,才开始执行队列中下一个代码块。
注意:
a.当我们向全局队列提交一个任务后,程序不会阻塞,因为全局队列中的任务不是在主线程中执行的。但是有个例外,当一个任务以dispatch_sync的方式提交到全局或串行队列的话,IOS可能会在当前线程中(可能在主线程中)去执行,这取决于当前提交代码所在函数的环境。
上面是苹果官方文档的说明,下面例子会详细说明。
b.如果我们以synchronously的方式分别向两个同步队列提交了两个任务,这两个任务是并行的,因为全局队列之间是并行的。
c.如果我们有A,B两个任务,想当A执行完后B再执行的话,我们可以以同步方式向同一个队列中来提交我们的任务。
dispatch_queue_t dispatch_get_global_queue(
long priority,
unsigned long flags);
第一个参数是优先级,优先级越高,获得的CPU的时间片也就越多,对列中的任务执行的也就越快。
第二个参数暂时无意义,保留参数。
- (void) viewDidLoad
{
void (^printFrom1To100)(void) = ^(void)
{
NSUInteger counter = 0;
for (counter=1;counter<100; counter++)
{
NSLog(@"Counter = %lu- Thread=%@",(unsigned long)counter,[NSThread currentThread]);
}
};
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(concurrentQueue, printFrom1To100);
dispatch_sync(concurrentQueue, printFrom1To100);
}
输出结果:
2013-11-08 15:33:01.252 GCDDemo[8649:70b] Counter = 1- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.254 GCDDemo[8649:70b] Counter = 2- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.255 GCDDemo[8649:70b] Counter = 3- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.256 GCDDemo[8649:70b] Counter = 4- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.257 GCDDemo[8649:70b] Counter = 5- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.258 GCDDemo[8649:70b] Counter = 6- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.259 GCDDemo[8649:70b] Counter = 7- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.259 GCDDemo[8649:70b] Counter = 8- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.260 GCDDemo[8649:70b] Counter = 9- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.261 GCDDemo[8649:70b] Counter = 10- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
………...
2013-11-08 15:33:01.338 GCDDemo[8649:70b] Counter = 1- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.339 GCDDemo[8649:70b] Counter = 2- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.339 GCDDemo[8649:70b] Counter = 3- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.340 GCDDemo[8649:70b] Counter = 4- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.340 GCDDemo[8649:70b] Counter = 5- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.341 GCDDemo[8649:70b] Counter = 6- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.341 GCDDemo[8649:70b] Counter = 7- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.342 GCDDemo[8649:70b] Counter = 8- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.343 GCDDemo[8649:70b] Counter = 9- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
2013-11-08 15:33:01.344 GCDDemo[8649:70b] Counter = 10- Thread=<NSThread: 0x8a80280>{name = (null), num = 1}
………..
上面我们定义了一个代码块,里面执行一个for循环,并打印当前和主线程的线程号。然后我们以同步的方式向全局队列提交了两次,观察结果我们发现:
a.Counter按照顺序1-100输出两遍,说明两个代码块是同步执行的,在前一个执行完后,后面一个才开始执行,也就是说第一个代码块的执行阻塞了全局队列(从而导致第二个代码块阻塞).
b.当前的线程号是1,说明该代码是在主线程中执行
以上的结果印证了刚才所讲的,“当一个任务以dispatch_sync的方式提交到全局或串行队列的话,IOS可能会在当前线程中(可能在主线程中)去执行”,我们在- (void) viewDidLoad函数中提交的代码块,而这个函数是在主线程中执行的,我们的代码块也在主线程中执行。
下面以异步的方式来提交任务:
- (void) viewDidLoad
{
void (^printFrom1To100)(void) = ^(void)
{
NSUInteger counter = 0;
for (counter=1;counter<100; counter++)
{
NSLog(@"Counter = %lu- Thread=%@",(unsigned long)counter,[NSThread currentThread]);
}
};
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, printFrom1To100);
dispatch_async(concurrentQueue, printFrom1To100);
}
输出结果:
2013-11-08 15:44:33.663 GCDDemo[8697:1303] Counter = 1- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.663 GCDDemo[8697:3207] Counter = 1- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.666 GCDDemo[8697:1303] Counter = 2- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.666 GCDDemo[8697:3207] Counter = 2- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.672 GCDDemo[8697:1303] Counter = 3- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.674 GCDDemo[8697:1303] Counter = 4- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.674 GCDDemo[8697:1303] Counter = 5- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.673 GCDDemo[8697:3207] Counter = 3- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.674 GCDDemo[8697:1303] Counter = 6- Thread=<NSThread: 0x8b0cdc0>{name = (null), num = 2}
2013-11-08 15:44:33.674 GCDDemo[8697:3207] Counter = 4- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.675 GCDDemo[8697:3207] Counter = 5- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.677 GCDDemo[8697:3207] Counter = 6- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.677 GCDDemo[8697:3207] Counter = 7- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.678 GCDDemo[8697:3207] Counter = 8- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
2013-11-08 15:44:33.678 GCDDemo[8697:3207] Counter = 9- Thread=<NSThread: 0x8a39320>{name = (null), num = 3}
观察发现:
a.Counter中的数字交错输出,说明两个代码块异步方式并行运行
b.线程号分别为2和3.,说明两个代码块在不同的线程中执行(非主线程)。
下面来看一个综合例子,下载一个图片 ,当图片下载完后在视图中显示图片,这种场景也是我们在项目中经常要用到的。
大概的代码实现框架如下:
dispatch_queue_t concurrentQueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^(void)
{
__block UIImage *image = nil;
dispatch_sync(concurrentQueue, ^(void)
{
/* Download the image here */
});
dispatch_sync(dispatch_get_main_queue(), ^(void)
{
/* Show the image to the user here on the main queue*/
});
});
分析:
我们以异步的方式来提交整个代码块,这样的话代码块的执行不会阻塞下面的代码。在代码块内我们又以同步的方式来执行了两个代码块,所以当执行“ Download the image here”代码块的时候它会阻塞后面的代码的执行,当下载完毕后,我们在主队列中同步的方式来显示图片(对所有涉及UI相关操作必须在主线程中去执行)。
三 自定义队列
直接看代码:
首先创建自己的串行队列:
dispatch_queue_t firstSerialQueue = dispatch_queue_create("com.pixolity.GCD.serialQueue1", DISPATCH_QUEUE_SERIAL );
异步方式向队列中提交三个代码块:
dispatch_async(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"First iteration, counter = %lu", (unsigned long)counter);
}
});
dispatch_async(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);
}
});
dispatch_async(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);
}
});
输出结果:
2013-11-08 17:16:27.721 GCDDemo[9081:2b03] First iteration, counter = 0 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.724 GCDDemo[9081:2b03] First iteration, counter = 1 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.724 GCDDemo[9081:2b03] First iteration, counter = 2 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.725 GCDDemo[9081:2b03] First iteration, counter = 3 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.725 GCDDemo[9081:2b03] First iteration, counter = 4 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.726 GCDDemo[9081:2b03] Sencond iteration, counter = 0 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.726 GCDDemo[9081:2b03] Sencond iteration, counter = 1 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.727 GCDDemo[9081:2b03] Sencond iteration, counter = 2 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.727 GCDDemo[9081:2b03] Sencond iteration, counter = 3 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.727 GCDDemo[9081:2b03] Sencond iteration, counter = 4 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.745 GCDDemo[9081:2b03] Third iteration, counter = 0 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.745 GCDDemo[9081:2b03] Third iteration, counter = 1 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.746 GCDDemo[9081:2b03] Third iteration, counter = 2 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.747 GCDDemo[9081:2b03] Third iteration, counter = 3 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
2013-11-08 17:16:27.751 GCDDemo[9081:2b03] Third iteration, counter = 4 Thread=<NSThread: 0x8b91210>{name = (null), num = 2}
同步的方式提交代码块:
dispatch_sync(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"####:sync counter = %lu Thread=%@", (unsigned long)counter,[NSThread currentThread]);
}
});
2013-11-08 17:19:00.818 GCDDemo[9110:70b] ####:sync counter = 0 Thread=<NSThread: 0x8a81110>{name = (null), num = 1}
2013-11-08 17:19:00.819 GCDDemo[9110:70b] ####:sync counter = 1 Thread=<NSThread: 0x8a81110>{name = (null), num = 1}
2013-11-08 17:19:00.819 GCDDemo[9110:70b] ####:sync counter = 2 Thread=<NSThread: 0x8a81110>{name = (null), num = 1}
2013-11-08 17:19:00.820 GCDDemo[9110:70b] ####:sync counter = 3 Thread=<NSThread: 0x8a81110>{name = (null), num = 1}
2013-11-08 17:19:00.842 GCDDemo[9110:70b] ####:sync counter = 4 Thread=<NSThread: 0x8a81110>{name = (null), num = 1}
然后创建自己的同步队列:
dispatch_queue_t
firstSerialQueue =
dispatch_queue_create
(
"com.pixolity.GCD.serialQueue1"
,
DISPATCH_QUEUE_CONCURRENT
);
dispatch_async
(firstSerialQueue, ^(
void
)
{
NSUInteger
counter =
0
;
for
(counter =
0
;counter <
5
;counter++)
{
NSLog
(
@"First iteration, counter = %lu Thread=%@"
, (
unsigned
long
)counter,[
NSThread
currentThread
]);
}
});
dispatch_async
(firstSerialQueue, ^(
void
)
{
NSUInteger
counter =
0
;
for
(counter =
0
;counter <
5
;counter++)
{
NSLog
(
@"Sencond iteration, counter = %lu Thread=%@"
, (
unsigned
long
)counter,[
NSThread
currentThread
]);
}
});
dispatch_async(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"Third iteration, counter = %lu Thread=%@", (unsigned long)counter,[NSThread currentThread]);
}
});
输出结果:
2013-11-11 16:52:00.429 GCDDemo[1302:3703] Sencond iteration, counter = 0 Thread=<NSThread: 0x8b93730>{name = (null), num = 3}
2013-11-11 16:52:00.429 GCDDemo[1302:3307] First iteration, counter = 0 Thread=<NSThread: 0x8a5ddf0>{name = (null), num = 2}
2013-11-11 16:52:00.433 GCDDemo[1302:3307] First iteration, counter = 1 Thread=<NSThread: 0x8a5ddf0>{name = (null), num = 2}
2013-11-11 16:52:00.433 GCDDemo[1302:3703] Sencond iteration, counter = 1 Thread=<NSThread: 0x8b93730>{name = (null), num = 3}
2013-11-11 16:52:00.434 GCDDemo[1302:3307] First iteration, counter = 2 Thread=<NSThread: 0x8a5ddf0>{name = (null), num = 2}
2013-11-11 16:52:00.435 GCDDemo[1302:3703] Sencond iteration, counter = 2 Thread=<NSThread: 0x8b93730>{name = (null), num = 3}
2013-11-11 16:52:00.436 GCDDemo[1302:3307] First iteration, counter = 3 Thread=<NSThread: 0x8a5ddf0>{name = (null), num = 2}
2013-11-11 16:52:00.436 GCDDemo[1302:3703] Sencond iteration, counter = 3 Thread=<NSThread: 0x8b93730>{name = (null), num = 3}
2013-11-11 16:52:00.437 GCDDemo[1302:3c03] Third iteration, counter = 0 Thread=<NSThread: 0x8a5a970>{name = (null), num = 4}
2013-11-11 16:52:00.438 GCDDemo[1302:3307] First iteration, counter = 4 Thread=<NSThread: 0x8a5ddf0>{name = (null), num = 2}
2013-11-11 16:52:00.439 GCDDemo[1302:3c03] Third iteration, counter = 1 Thread=<NSThread: 0x8a5a970>{name = (null), num = 4}
2013-11-11 16:52:00.439 GCDDemo[1302:3703] Sencond iteration, counter = 4 Thread=<NSThread: 0x8b93730>{name = (null), num = 3}
2013-11-11 16:52:00.440 GCDDemo[1302:3c03] Third iteration, counter = 2 Thread=<NSThread: 0x8a5a970>{name = (null), num = 4}
2013-11-11 16:52:00.441 GCDDemo[1302:3c03] Third iteration, counter = 3 Thread=<NSThread: 0x8a5a970>{name = (null), num = 4}
2013-11-11 16:52:00.441 GCDDemo[1302:3c03] Third iteration, counter = 4 Thread=<NSThread: 0x8a5a970>{name = (null), num = 4}
同步的方式提交代码块:
dispatch_sync(firstSerialQueue, ^(void)
{
NSUInteger counter = 0;
for (counter = 0;counter < 5;counter++)
{
NSLog(@"####:sync counter = %lu Thread=%@", (unsigned long)counter,[NSThread currentThread]);
}
});
2013-11-11 17:02:02.221 GCDDemo[1348:70b] ####:sync counter = 0 Thread=<NSThread: 0x8a406c0>{name = (null), num = 1}
2013-11-11 17:02:02.221 GCDDemo[1348:70b] ####:sync counter = 1 Thread=<NSThread: 0x8a406c0>{name = (null), num = 1}
2013-11-11 17:02:02.222 GCDDemo[1348:70b] ####:sync counter = 2 Thread=<NSThread: 0x8a406c0>{name = (null), num = 1}
2013-11-11 17:02:02.222 GCDDemo[1348:70b] ####:sync counter = 3 Thread=<NSThread: 0x8a406c0>{name = (null), num = 1}
2013-11-11 17:02:02.223 GCDDemo[1348:70b] ####:sync counter = 4 Thread=<NSThread: 0x8a406c0>{name = (null), num = 1}
观察发现:
1.dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
第一个参数是一个c语言形式的字符串
第二个参数在可以是 DISPATCH_QUEUE_SERIAL (orNULL
) 来创建串行队列
DISPATCH_QUEUE_CONCURRENT 来创建同步队列
2.自定义串行队列是严格按照提交FIFO的原则来执行任务,不管你是以何种方式来提交待任务。
3.以同步方式提交的任务都会在当前提交的线程中去执行。例如,在主线程中以同步方式提交的代码块,线程号是1(主线程)。
4.以异步方式提交的任务,它们都不会在当前线程中执行,对于串行队列来说它们都会在同一个线程中去执行,而对于同步队列来说它们分别在不同的线程中去执行。
5.串行队列是系统相关的,为了和别的程序串行队列不冲突,建议使用反转DNS来命名串行队列,以保证唯一性。
6. 自定义串行队列中的任务是串行执行的(不管以何种方式进行提交)。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mute。
四 延迟提交代码块
直接看代码:
NSLog(@"here......");
double delayInSeconds = 5.0;
dispatch_time_t delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(delayInNanoSeconds, concurrentQueue, ^(void)
{
NSLog(@"delay execute......");
});
输出结果:
2013-11-08 17:07:00.215 GCDDemo[9036:70b] here......
2013-11-08 17:07:05.252 GCDDemo[9036:1303] delay execute......
观察结果:发现代码块块的执行时间比之前的代码晚了5秒钟void
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
第一个参数用来指定延迟的事件,用dispatch_time函数来构建
第二个参数用来指定队列
第三个参数用来指定要运行的代码块
五 总结
使用GCD主要把握好每种队列的特点,以及任务提交方式和实际运行线程的对应关系。
向主队列不管以何种方式提交的代码块总会在主线程中运行,而且为了防止在主线程内提交代码块阻塞主线程,最好以异步的方式来提交代码块;以同步的方式向全局队列和自定义队列提交的代码块,会在当前提交所在线程内执行代码块,不会在另外的线程来执行,以异步的方式提交到全局队列的代码块和自定义队列中的代码块不会阻塞当前线程,会在另外线程中执行;不管以同步还是异步的方式向自定义串行队列提交代码块,都会严格按照提交顺序去执行(one by one);以异步方式向自定义同步队列,会为每个代码块创建一个线程,而以异步方式向自定义串行队列提交的代码块会在同一个线程中执行。