1、 同步&异步 串行&并行
以上两两相对的概念是比较容易理解的,一旦混合起来使用就容易错误。下面是我的个人理解。
串行就是顺序执行(在一个线程中执行任务),并行相对应的不是顺序执行(会创建不同的线程去执行任务)。 同步需要等待执行,有结果才会返回,异步相对应的不会等待结果,任何时候有结果再返回都可以。
不管是串行队列(SerialQueue)还是并行队列(ConcurrencyQueue),都是FIFO队列。也就意味着,任务一定是一个一个地,按照先进先出的顺序来执行。
串行队列下,不管是同步还是异步都是按照顺序的的执行,异步只是不在当前线程。 并行队列下,同步执行时因为需要等待,所以就在一个线程里执行就变成了串行的执行结果;异步执行并不需要等待而且系统会创建不同的线程去执行,不同线程中任务执行时间并不确定。(GCD可以通过信号量设置并行队列最大执行数,因为过多创建线程也会影响性能)
//定义一个信号量,初始化为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
//同时执行100个任务
for (int i = 0; i < 100; i++)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//当前信号量-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务%d执行",i+1);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrlString]];
dispatch_async(dispatch_get_main_queue(), ^{
//TODO:刷新界面
});
//当前信号量+1
dispatch_semaphore_signal(semaphore);
});
//来源 http://www.jianshu.com/p/ea72d1ded383
}
1-1、图示
串行 并行 + 同步

串行 异步

并行 异步

1-2、示例代码
分别是 串行队列 “同步执行,异步执行” 和 并行队列 “同步执行,异步执行” 先执行同步和先执行异步结果也是不一样的,从结果我们可以看出,当前线程创建一个同步执行的串行\并行队列都会使当前线程阻塞,一定要慎用。iOS中千万注意是不是主线程是否使用了同步。
- (void)serialQueue1 {
dispatch_queue_t queue = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
NSLog(@"serialQueue 同步 - %@",[NSDate date]);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"serialQueue 同步 1 %@",[NSDate date]);
});
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"serialQueue 同步 2 %@",[NSDate date]);
});
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"serialQueue 同步 3 %@",[NSDate date]);
});
}
- (void)serialQueue2 {
dispatch_queue_t queue = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
NSLog(@"serialQueue 异步 - %@",[NSDate date]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"serialQueue 异步 1 %@",[NSDate date]);
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"serialQueue 异步 2 %@",[NSDate date]);
});
dispatch_async(queue, ^{
NSLog(@"serialQueue 异步 3 %@",[NSDate date]);
});
}
// 执行结果 串行队列,先同步执行
2017-03-23 18:38:31.069 QUEUE[4001:169705] serialQueue 同步 - 2017-03-23 10:38:31 +0000,<NSThread: 0x600000068e80>{number = 1, name = main}
2017-03-23 18:38:32.069 QUEUE[4001:169705] serialQueue 同步 1 2017-03-23 10:38:32 +0000,<NSThread: 0x600000068e80>{number = 1, name = main}
2017-03-23 18:38:34.070 QUEUE[4001:169705] serialQueue 同步 2 2017-03-23 10:38:34 +0000,<NSThread: 0x600000068e80>{number = 1, name = main}
2017-03-23 18:38:35.072 QUEUE[4001:169705] serialQueue 同步 3 2017-03-23 10:38:35 +0000,<NSThread: 0x600000068e80>{number = 1, name = main}
2017-03-23 18:38:35.072 QUEUE[4001:169705] serialQueue 异步 - 2017-03-23 10:38:35 +0000,<NSThread: 0x600000068e80>{number = 1, name = main}
2017-03-23 18:38:36.076 QUEUE[4001:169752] serialQueue 异步 1 2017-03-23 10:38:36 +0000,<NSThread: 0x60000006eec0>{number = 3, name = (null)}
2017-03-23 18:38:38.082 QUEUE[4001:169752] serialQueue 异步 2 2017-03-23 10:38:38 +0000,<NSThread: 0x60000006eec0>{number = 3, name = (null)}
2017-03-23 18:38:38.083 QUEUE[4001:169752] serialQueue 异步 3 2017-03-23 10:38:38 +0000,<NSThread: 0x60000006eec0>{number = 3, name = (null)}
// 执行结果 串行队列,先异步执行
2017-03-23 18:45:08.531 QUEUE[4102:173791] serialQueue 异步 - 2017-03-23 10:45:08 +0000,<NSThread: 0x6000000712c0>{number = 1, name = main}
2017-03-23 18:45:08.531 QUEUE[4102:173791] serialQueue 同步 - 2017-03-23 10:45:08 +0000,<NSThread: 0x6000000712c0>{number = 1, name = main}
2017-03-23 18:45:09.533 QUEUE[4102:173791] serialQueue 同步 1 2017-03-23 10:45:09 +0000,<NSThread: 0x6000000712c0>{number = 1, name = main}
2017-03-23 18:45:09.536 QUEUE[4102:173832] serialQueue 异步 1 2017-03-23 10:45:09 +0000,<NSThread: 0x6080000728c0>{number = 3, name = (null)}
2017-03-23 18:45:11.535 QUEUE[4102:173791] serialQueue 同步 2 2017-03-23 10:45:11 +0000,<NSThread: 0x6000000712c0>{number = 1, name = main}
2017-03-23 18:45:11.541 QUEUE[4102:173832] serialQueue 异步 2 2017-03-23 10:45:11 +0000,<NSThread: 0x6080000728c0>{number = 3, name = (null)}
2017-03-23 18:45:11.542 QUEUE[4102:173832] serialQueue 异步 3 2017-03-23 10:45:11 +0000,<NSThread: 0x6080000728c0>{number = 3, name = (null)}
2017-03-23 18:45:12.536 QUEUE[4102:173791] serialQueue 同步 3 2017-03-23 10:45:12 +0000,<NSThread: 0x6000000712c0>{number = 1, name = main}
- (void)concurrentQueue1 {
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"concurrentQueue 同步 - %@,%@",[NSDate date],[NSThread currentThread]);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"concurrentQueue 同步 1 %@,%@",[NSDate date],[NSThread currentThread]);
});
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"concurrentQueue 同步 2 %@,%@",[NSDate date],[NSThread currentThread]);
});
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"concurrentQueue 同步 3 %@,%@",[NSDate date],[NSThread currentThread]);
});
}
- (void)concurrentQueue2 {
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"concurrentQueue 异步 - %@,%@",[NSDate date],[NSThread currentThread]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"concurrentQueue 异步 1 %@,%@",[NSDate date],[NSThread currentThread]);
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"concurrentQueue 异步 2 %@,%@",[NSDate date],[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"concurrentQueue 异步 3 %@,%@",[NSDate date],[NSThread currentThread]);
});
}
// 执行结果 串行队列,先同步执行
2017-03-23 19:09:52.637 QUEUE[4519:191342] concurrentQueue 同步 - 2017-03-23 11:09:52 +0000,<NSThread: 0x6180000789c0>{number = 1, name = main}
2017-03-23 19:09:53.638 QUEUE[4519:191342] concurrentQueue 同步 1 2017-03-23 11:09:53 +0000,<NSThread: 0x6180000789c0>{number = 1, name = main}
2017-03-23 19:09:55.640 QUEUE[4519:191342] concurrentQueue 同步 2 2017-03-23 11:09:55 +0000,<NSThread: 0x6180000789c0>{number = 1, name = main}
2017-03-23 19:09:56.642 QUEUE[4519:191342] concurrentQueue 同步 3 2017-03-23 11:09:56 +0000,<NSThread: 0x6180000789c0>{number = 1, name = main}
2017-03-23 19:09:56.642 QUEUE[4519:191342] concurrentQueue 异步 - 2017-03-23 11:09:56 +0000,<NSThread: 0x6180000789c0>{number = 1, name = main}
2017-03-23 19:09:56.643 QUEUE[4519:191406] concurrentQueue 异步 3 2017-03-23 11:09:56 +0000,<NSThread: 0x61800007c740>{number = 3, name = (null)}
2017-03-23 19:09:57.645 QUEUE[4519:191388] concurrentQueue 异步 1 2017-03-23 11:09:57 +0000,<NSThread: 0x600000260080>{number = 4, name = (null)}
2017-03-23 19:09:58.648 QUEUE[4519:191391] concurrentQueue 异步 2 2017-03-23 11:09:58 +0000,<NSThread: 0x600000260040>{number = 5, name = (null)}
// 执行结果 串行队列,先异步执行
2017-03-23 19:08:43.658 QUEUE[4474:189774] concurrentQueue 异步 - 2017-03-23 11:08:43 +0000,<NSThread: 0x608000260080>{number = 1, name = main}
2017-03-23 19:08:43.658 QUEUE[4474:189774] concurrentQueue 同步 - 2017-03-23 11:08:43 +0000,<NSThread: 0x608000260080>{number = 1, name = main}
2017-03-23 19:08:43.658 QUEUE[4474:189822] concurrentQueue 异步 3 2017-03-23 11:08:43 +0000,<NSThread: 0x600000262380>{number = 3, name = (null)}
2017-03-23 19:08:44.660 QUEUE[4474:189774] concurrentQueue 同步 1 2017-03-23 11:08:44 +0000,<NSThread: 0x608000260080>{number = 1, name = main}
2017-03-23 19:08:44.660 QUEUE[4474:189820] concurrentQueue 异步 1 2017-03-23 11:08:44 +0000,<NSThread: 0x618000267300>{number = 4, name = (null)}
2017-03-23 19:08:45.662 QUEUE[4474:189833] concurrentQueue 异步 2 2017-03-23 11:08:45 +0000,<NSThread: 0x6100002626c0>{number = 5, name = (null)}
2017-03-23 19:08:46.661 QUEUE[4474:189774] concurrentQueue 同步 2 2017-03-23 11:08:46 +0000,<NSThread: 0x608000260080>{number = 1, name = main}
2017-03-23 19:08:47.663 QUEUE[4474:189774] concurrentQueue 同步 3 2017-03-23 11:08:47 +0000,<NSThread: 0x608000260080>{number = 1, name = main}
2、同步&异步 阻塞&非阻塞
2-1、知乎
老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。 1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点傻 2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞) 老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。 3 老张把响水壶放到火上,立等水开。(异步阻塞) 老张觉得这样傻等意义不大 4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞) 老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。 作者:愚抄 链接:https://www.zhihu.com/question/19732473/answer/23434554 更多答案 https://www.zhihu.com/question/19732473
2-2、伯乐在线
同步/异步主要针对C端: 同步: 所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步: 异步的概念和同步相对。当c端一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。 例如 ajax请求(异步): 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕 阻塞/非阻塞主要针对S端: 阻塞 阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。 有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。 例如,我们在socket中调用recv函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。 快递的例子:比如到你某个时候到A楼一层(假如是内核缓冲区)取快递,但是你不知道快递什么时候过来,你又不能干别的事,只能死等着。但你可以睡觉(进程处于休眠状态),因为你知道快递把货送来时一定会给你打个电话(假定一定能叫醒你)。 非阻塞 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。 还是等快递的例子:如果用忙轮询的方法,每隔5分钟到A楼一层(内核缓冲区)去看快递来了没有。如果没来,立即返回。而快递来了,就放在A楼一层,等你去取。 来源 http://blog.jobbole.com/103290/?repeat=w3tc
上面的两个分析中,知乎的例子更好理解,但是解释对象伯乐在线更对。同步异步应该是对老张而言,阻塞非阻塞是对水壶而言
1)阻塞I/O(blocking I/O) 2)非阻塞I/O (nonblocking I/O)
- I/O复用(select 和poll) (I/O multiplexing) 4)信号驱动I/O (signal driven I/O (SIGIO)) 5)异步I/O (asynchronous I/O (the POSIX aio_functions)) 前四种都是同步,只有最后一种才是异步IO。
以上两部分都有关于 Linux下的五种I/O模型的解释