多线程编程--Grand Central Dispatch(一)

时间:2021-03-16 23:32:42
Grand Central Dispatch 所有的dispatch对象都是Objective-C对象,遵循引用计数的内存管理法则;如果使用ARC开发,dispatch对象可以自动retained 和 released,因此不用关心其内存管理问题。但是如果是MRC(手动管理内存),需要手动管理内存,使用函数 dispatch_retain 和 dispatch_release而不是使用retain/release方法。 使用思路大体是:定义任务,然后交给GCD的quene去执行。这里的任务,是指要执行的代码,比如读取数据、下载图片等等。任务是自己定义的,主要是获取合适的Quene去执行任务,Quene分下两种:串行队列(Serial)和并发队列(Concurrent)。 一、简介 1、串行队列(Serial a、任务安装先进先出(FIFO)顺序执行,拥有一个线程 b、通常用来同步地写入数据,比如说UserDefaults中写入数据的时候,通常会同步写入。 c、串行队列可以创建多个,但是这几个队列的任务的执行顺序是并发执行的。 2、并发队列(Concurrent a、任务并发执行,但是执行顺序和添加到队列的顺序一样,结束顺序不固定。线程数量由系统根据当时资源决定。 b、并发队列是可以创建多个
二、获取方式 1、直接创建,通过dispatch_queue_create(constchar *label,dispatch_queue_attr_t attr)
创建,第一个参数是quene的名称,可以为NULL;第二个参数是指定队列类型的,传DISPATCH_QUEUE_SERIAL或者 NULL可以创建串行队列,传递DISPATCH_QUEUE_CONCURRENT可以创建并发队列,如代码所示
    //串行队列
dispatch_queue_t serialQuene = dispatch_queue_create("serialQuene", NULL);
dispatch_queue_t serialQuene2 = dispatch_queue_create("serialQuene", DISPATCH_QUEUE_SERIAL);
//并发队列
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);

   2、直接获取
   a、串行队列,代表为主线程队列dispatch_get_main_queue
dispatch_queue_t mainQuene = dispatch_get_main_queue();


   b、并发队列,代表为dispatch_get_main_queue dispatch_queue_t dispatch_get_global_queue( long identifier, unsigned long flags);
identifier表示队列执行的优先级,优先级分四种:

 *  - DISPATCH_QUEUE_PRIORITY_HIGH         

 *  - DISPATCH_QUEUE_PRIORITY_DEFAULT      

 *  - DISPATCH_QUEUE_PRIORITY_LOW        

 *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND  

flags参数作为备用参数,一般传0.


    dispatch_queue_t concurrentQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t concurrentQuene2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t concurrentQuene3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t concurrentQuene4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);


   c、当前队列dispatch_get_current_queue 作为测试用的接口,已经废弃了。不建议使用。

注意:对main_quene和global_quene执行 dispatch_retain  dispatch_releasedispatch_suspenddispatch_resume  dispatch_set_context操作都将被忽略。

     dispatch_queue_t currentQuene = dispatch_get_current_queue();

三、任务执行方式
任务执行方式分为两种:同步执行(dispatch_sync)和异步执行(dispatch_async) 1、异步执行dispatch_async( dispatch_queue_t queue, dispatch_block_t block);其中quene,是任务即将执行的队列,决定执行方式(并行还串行),block为任务内容,然后以block的形式放到quene中执行。任务一旦添加到队列立即执行,然后进行下一个任务,等到各自任务执行完毕自行结束完毕 2、同步执行dispatch_sync( dispatch_queue_t queue, dispatch_block_t block);使用方式和异步执行一样。和异步不同的是,任务添加并执行后,将一直等待block完成后再进行下一个任务(block中定义的task),需要注意的是不能在串行队列未完成的任务中提交同步任务,会互相等待造成死锁。
四、串行、并行队列和同步、异步的排列组合使用 1、串行队列执行同步任务 代码按照任务添加的顺序执行,完成一个后再进行下一个,代码和运行结果如下
    dispatch_queue_t serialQuene = dispatch_queue_create("serialQuene", DISPATCH_QUEUE_SERIAL);

for (int i = 0; i<100; i++)
{
dispatch_sync(serialQuene, ^{
NSLog(@"开始-%d--%@",i,[NSThread currentThread]);

//将一张大的图片转换需要花费略长的时间
UIImage *img = [UIImage imageNamed:@"1"];
UIImageJPEGRepresentation(img, 1);

NSLog(@"结束-%d--%@",i,[NSThread currentThread]);
});
}

2016-07-13 09:58:49.711 GCDTest[1157:36191] 开始-0--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.713 GCDTest[1157:36191] 结束-0--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.713 GCDTest[1157:36191] 开始-1--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.713 GCDTest[1157:36191] 结束-1--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.713 GCDTest[1157:36191] 开始-2--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 结束-2--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 开始-3--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 结束-3--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 开始-4--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 结束-4--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.714 GCDTest[1157:36191] 开始-5--<NSThread: 0x7f9108603910>{number = 1, name = main}
2016-07-13 09:58:49.732 GCDTest[1157:36191] 结束-5--<NSThread: 0x7f9108603910>{number = 1, name = main}

但是请注意:不能够在串行队列未完成的任务中添加同步任务,会造成死锁
    dispatch_queue_t serialQuene = dispatch_queue_create("serialQuene", DISPATCH_QUEUE_SERIAL);

NSLog(@"开始前--%@",[NSThread currentThread]);

//这里dispatch_async也一样,串行队列当前只能执行一个任务
dispatch_sync(serialQuene, ^{
//任务A
NSLog(@"A等待B执行完,A才算结束;队列进行下个任务--%@",[NSThread currentThread]);

dispatch_sync(serialQuene, ^{
//任务B
NSLog(@"B已添加到队列,等待A完成后,队列才能执行--%@",[NSThread currentThread]);
//A、B相互等待,造成死锁
});
NSLog(@"结束--%@",[NSThread currentThread]);
});
NSLog(@"结束后--%@",[NSThread currentThread]);

2016-07-13 10:47:05.933 GCDTest[1345:55772] 开始前--<NSThread: 0x7fc8c0400ab0>{number = 1, name = main}
2016-07-13 10:47:05.934 GCDTest[1345:55772] 结束后--<NSThread: 0x7fc8c0400ab0>{number = 1, name = main}
2016-07-13 10:47:05.934 GCDTest[1345:55810] A等待B执行完,A才算结束;队列进行下个任务--<NSThread: 0x7fc8c0416af0>{number = 2, name = (null)}

关于此类现象,苹果的官方说明是这样的,就不翻译了
You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.

此时就可以解释一个经典的死锁代码,主线程执行同步任务死锁
    dispatch_queue_t mainQuene = dispatch_get_main_queue();
//任务A
//主线程是串行队列,等待A执行完(B必须执行完)再执行B
dispatch_sync(mainQuene, ^{
//任务B,等待A执行完
NSLog(@"死锁,不执行");
});

此代码没有输出。
只要B不等待A就可以破除死锁
    dispatch_queue_t serialQuene = dispatch_queue_create("serialQuene", DISPATCH_QUEUE_SERIAL);

NSLog(@"开始前--%@",[NSThread currentThread]);

//这里dispatch_async也一样,串行队列当前只能执行一个任务
dispatch_sync(serialQuene, ^{
//任务A
NSLog(@"A等待B执行完,A才算结束;队列进行下个任务--%@",[NSThread currentThread]);

dispatch_async(serialQuene, ^{
//任务B
// NSLog(@"B已添加到队列,等待A完成后,队列才能执行--%@",[NSThread currentThread]);

NSLog(@"B已添加到队列,不等待A直接向下执行--%@",[NSThread currentThread]);
//A、B相互等待,造成死锁
});
NSLog(@"结束--%@",[NSThread currentThread]);
});
NSLog(@"结束后--%@",[NSThread currentThread]);

2016-07-13 11:07:35.160 GCDTest[1417:65856] 开始前--<NSThread: 0x7fa392700a90>{number = 1, name = main}
2016-07-13 11:07:35.161 GCDTest[1417:65856] A等待B执行完,A才算结束;队列进行下个任务--<NSThread: 0x7fa392700a90>{number = 1, name = main}
2016-07-13 11:07:35.162 GCDTest[1417:65856] 结束--<NSThread: 0x7fa392700a90>{number = 1, name = main}
2016-07-13 11:07:35.162 GCDTest[1417:65856] 结束后--<NSThread: 0x7fa392700a90>{number = 1, name = main}
2016-07-13 11:07:35.162 GCDTest[1417:65888] B已添加到队列,等待A完成后,队列才能执行--<NSThread: 0x7fa3927530a0>{number = 2, name = (null)}

2、串行队列执行异步任务
任务还是一个接一个执行的,但是不会阻塞线程,例如主线程中进行,不影响UI的变化、页面交互。
dispatch_queue_t mainQuene = dispatch_get_main_queue();
for (int i = 0; i<100; i++)
{
dispatch_async(mainQuene, ^{
NSLog(@"开始-%d--%@",i,[NSThread currentThread]);

//将一张大的图片转换需要花费略长的时间
UIImage *img = [UIImage imageNamed:@"1"];
UIImageJPEGRepresentation(img, 1);

NSLog(@"结束-%d--%@",i,[NSThread currentThread]);
});
}

2016-07-13 11:39:21.319 GCDTest[1671:86044] 开始-0--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:22.979 GCDTest[1671:86044] 结束-0--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:22.979 GCDTest[1671:86044] 开始-1--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:23.482 GCDTest[1671:86044] 结束-1--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:23.482 GCDTest[1671:86044] 开始-2--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:23.944 GCDTest[1671:86044] 结束-2--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:23.945 GCDTest[1671:86044] 开始-3--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:24.408 GCDTest[1671:86044] 结束-3--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:24.408 GCDTest[1671:86044] 开始-4--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:24.917 GCDTest[1671:86044] 结束-4--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:24.917 GCDTest[1671:86044] 开始-5--<NSThread: 0x7fa7c0505500>{number = 1, name = main}
2016-07-13 11:39:25.394 GCDTest[1671:86044] 结束-5--<NSThread: 0x7fa7c0505500>{number = 1, name = main}

3、并发队列执行同步任务
任务按照添加的顺序,一个一个任务的顺序执行。
dispatch_queue_t concurrentQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

for (int i = 0; i<5; i++)
{
dispatch_sync(concurrentQuene, ^{
NSLog(@"开始-%d",i);
UIImage *img = [UIImage imageNamed:@"1"];
UIImageJPEGRepresentation(img, 1);
NSLog(@"结束-%d",i);
});
}

2016-07-13 16:19:58.922 GCDTest[2680:185888] 开始-0
2016-07-13 16:20:00.696 GCDTest[2680:185888] 结束-0
2016-07-13 16:20:00.696 GCDTest[2680:185888] 开始-1
2016-07-13 16:20:01.186 GCDTest[2680:185888] 结束-1
2016-07-13 16:20:01.186 GCDTest[2680:185888] 开始-2
2016-07-13 16:20:01.679 GCDTest[2680:185888] 结束-2
2016-07-13 16:20:01.679 GCDTest[2680:185888] 开始-3
2016-07-13 16:20:02.151 GCDTest[2680:185888] 结束-3
2016-07-13 16:20:02.151 GCDTest[2680:185888] 开始-4
2016-07-13 16:20:02.624 GCDTest[2680:185888] 结束-4


4、并发队列执行异步任务 任务按照随机顺序开始执行,结束顺序也是随机。
dispatch_queue_t concurrentQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i<5; i++)
{
dispatch_async(concurrentQuene, ^{

NSLog(@"开始-%d--%@",i,[NSThread currentThread]);

//将一张大的图片转换需要花费略长的时间
UIImage *img = [UIImage imageNamed:@"1"];
UIImageJPEGRepresentation(img, 1);

NSLog(@"结束-%d--%@",i,[NSThread currentThread]);
});
}


2016-07-13 15:48:38.674 GCDTest[2421:164319] 开始-0--<NSThread: 0x7f8b8bd1f310>{number = 2, name = (null)}
2016-07-13 15:48:38.674 GCDTest[2421:164314] 开始-1--<NSThread: 0x7f8b8bc55e20>{number = 3, name = (null)}
2016-07-13 15:48:38.674 GCDTest[2421:164323] 开始-2--<NSThread: 0x7f8b8be0b5c0>{number = 4, name = (null)}
2016-07-13 15:48:38.675 GCDTest[2421:164397] 开始-3--<NSThread: 0x7f8b8bf601b0>{number = 5, name = (null)}
2016-07-13 15:48:38.675 GCDTest[2421:164398] 开始-4--<NSThread: 0x7f8b8bd26420>{number = 6, name = (null)}
2016-07-13 15:48:40.904 GCDTest[2421:164319] 结束-0--<NSThread: 0x7f8b8bd1f310>{number = 2, name = (null)}
2016-07-13 15:48:40.913 GCDTest[2421:164397] 结束-3--<NSThread: 0x7f8b8bf601b0>{number = 5, name = (null)}
2016-07-13 15:48:40.915 GCDTest[2421:164323] 结束-2--<NSThread: 0x7f8b8be0b5c0>{number = 4, name = (null)}
2016-07-13 15:48:40.917 GCDTest[2421:164314] 结束-1--<NSThread: 0x7f8b8bc55e20>{number = 3, name = (null)}
2016-07-13 15:48:40.922 GCDTest[2421:164398] 结束-4--<NSThread: 0x7f8b8bd26420>{number = 6, name = (null)}