首页 > 系统 > iOS > 正文

iOS多线程GCD的总结

2019-11-06 09:44:48
字体:
来源:转载
供稿:网友

大家都知道,多线程分为Pthreads、NSThread、GCD和NSOperation & NSOperationQueue四套方案

接下来主要介绍GCD(Grand Central Dispatch) GCD:是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 c语言,不过由于使用了 Block(Swift里叫做闭包),使得使用起来更加方便,而且灵活。

一、任务和队列 在 GCD 中,加入了两个非常重要的概念: 任务 和 队列。

《1》任务:即操作,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别是 是否会创建新的线程。 同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕! 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。

《2》队列:用于存放任务。一共有两种队列, 串行队列 和 并行队列。 放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。 放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。

《3》总结:串行队列中:同步执行是在当前线程,一个一个执行;异步执行是在其他线程,一个一个执行 并行队列中:同步执行是在当前线程,一个一个执行;异步执行是开很多线程,一起执行

二、创建任务

《1》同步任务:会阻塞当前线程 (SYNC)

//OBJECTIVE-C dispatch_sync(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]); });//SWIFT dispatch_sync(<#queue#>, { () -> Void in PRintln(NSThread.currentThread()) })

《2》异步任务:不会阻塞当前线程 (ASYNC)

//OBJECTIVE-C dispatch_async(<#queue#>, ^{ //code here NSLog(@"%@", [NSThread currentThread]); });//SWIFT dispatch_async(<#queue#>, { () -> Void in //code here println(NSThread.currentThread()) })

为了更好的理解同步和异步,和各种队列的使用,下面看两个示例 示例一:

//主线程调用 swiftNSLog("之前 - %@", NSThread.currentThread())dispatch_sync(dispatch_get_main_queue(), { () -> Void in NSLog("sync - %@", NSThread.currentThread())})NSLog("之后 - %@", NSThread.currentThread())//oc NSLog(@"之前 - %@", [NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"sync - %@", [NSThread currentThread]); });NSLog(@"之后 - %@", [NSThread currentThread]);

输出结果: 只会打印第一句:之前 - {number = 1, name = main} ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。 解释: 同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。 那么这里的步骤就是:打印完第一句后,dispatch_sync 立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue 中,可是 main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,dispatch_sync 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。

示例二:

//swiftlet queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL) NSLog("之前 - %@", NSThread.currentThread()) dispatch_async(queue, { () -> Void in NSLog("sync之前 - %@", NSThread.currentThread()) dispatch_sync(queue, { () -> Void in NSLog("sync - %@", NSThread.currentThread()) }) NSLog("sync之后 - %@", NSThread.currentThread()) }) NSLog("之后 - %@", NSThread.currentThread()) //oc dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"之前 - %@", [NSThread currentThread]); dispatch_async(queue, ^{ NSLog(@"sync之前 - %@", [NSThread currentThread]); dispatch_sync(queue, ^{ NSLog(@"sync - %@", [NSThread currentThread]); }); NSLog(@"sync之后 - %@", [NSThread currentThread]); }); NSLog(@"之后 - %@", [NSThread currentThread]);

结果:2017-02-27 20:31:22.438 demo[95573:7473887] 之前 - {number = 1, name = main} 2017-02-27 20:31:22.438 demo[95573:7473887] 之后 - {number = 1, name = main} 2017-02-27 20:31:22.438 demo[95573:7473926] sync之前 - {number = 3, name = (null)}

很明显 sync - %@ 和 sync之后 - %@ 没有被打印出来!

原因如下: 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。 打印出 之前 - %@ 这句。 dispatch_async 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 之后 - %@这句, 另一台执行 Block 中的内容打印 sync之前 - %@ 这句。因为这两条是并行的,所以打印的先后顺序无所谓。 注意,高潮来了。现在的情况和上一个例子一样了。dispatch_sync同步执行,于是它所在的线程会被阻塞,一直等到 sync 里的任务执行完才会继续往下。于是 sync 就高兴的把自己 Block 中的任务放到 queue 中,可谁想 queue 是一个串行队列,一次执行一个任务,所以 sync 的 Block 必须等到前一个任务执行完毕,可万万没想到的是 queue 正在执行的任务就是被 sync 阻塞了的那个。于是又发生了死锁。所以 sync 所在的线程被卡死了。剩下的两句代码自然不会打印。

《3》队列组 队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。

OBJECTIVE-C//1.创建队列组dispatch_group_t group = dispatch_group_create();//2.创建队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//3.多次使用队列组的方法执行任务, 只有异步方法//3.1.执行3次循环dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 3; i++) { NSLog(@"group-01 - %@", [NSThread currentThread]); }});//3.2.主队列执行8次循环dispatch_group_async(group, dispatch_get_main_queue(), ^{ for (NSInteger i = 0; i < 8; i++) { NSLog(@"group-02 - %@", [NSThread currentThread]); }});//3.3.执行5次循环dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"group-03 - %@", [NSThread currentThread]); }});//4.都完成后会自动通知dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"完成 - %@", [NSThread currentThread]);});SWIFT//1.创建队列组let group = dispatch_group_create()//2.创建队列let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)//3.多次使用队列组的方法执行任务, 只有异步方法//3.1.执行3次循环dispatch_group_async(group, queue) { () -> Void in for _ in 0..<3 { NSLog("group-01 - %@", NSThread.currentThread()) }}//3.2.主队列执行8次循环dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in for _ in 0..<8 { NSLog("group-02 - %@", NSThread.currentThread()) }}//3.3.执行5次循环dispatch_group_async(group, queue) { () -> Void in for _ in 0..<5 { NSLog("group-03 - %@", NSThread.currentThread()) }}//4.都完成后会自动通知dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in NSLog("完成 - %@", NSThread.currentThread())}

结果如下: 2017-02-27 20:17:11.583 demo[95330:7457515] group-03 - {number = 4, name = (null)} 2017-02-27 20:17:11.583 demo[95330:7457514] group-01 - {number = 3, name = (null)} 2017-02-27 20:17:11.584 demo[95330:7457515] group-03 - {number = 4, name = (null)} 2017-02-27 20:17:11.585 demo[95330:7457514] group-01 - {number = 3, name = (null)} 2017-02-27 20:17:11.586 demo[95330:7457515] group-03 - {number = 4, name = (null)} 2017-02-27 20:17:11.587 demo[95330:7457514] group-01 - {number = 3, name = (null)} 2017-02-27 20:17:11.587 demo[95330:7457515] group-03 - {number = 4, name = (null)} 2017-02-27 20:17:11.588 demo[95330:7457515] group-03 - {number = 4, name = (null)} 2017-02-27 20:17:11.595 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.595 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.596 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.597 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.598 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.599 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.599 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.600 demo[95330:7457402] group-02 - {number = 1, name = main} 2017-02-27 20:17:11.601 demo[95330:7457402] 完成 - {number = 1, name = main}

三、创建队列

《1》主队列:这是一个特殊的 串行队列。主队列用于刷新 UI,任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。

//OBJECTIVE-C dispatch_queue_t queue = ispatch_get_main_queue(); //SWIFT let queue = ispatch_get_main_queue()

《2》自己创建的队列:既可以自己创建串行队列, 也可以创建并行队列。

//OBJECTIVE-C //串行队列 dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL); dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL); //并行队列 dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT); //SWIFT //串行队列 let queue = dispatch_queue_create("tk.bourne.testQueue", nil); let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL) //并行队列 let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)

其中第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。

《3》全局并行队列: 只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。

//OBJECTIVE-C dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //SWIFT let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

四、关于GCD,还有两个需要说的:

func dispatch_barrier_async(_ queue: dispatch_queue_t, _ block: dispatch_block_t):

这个方法重点是你传入的 queue,当你传入的 queue 是通过 DISPATCH_QUEUE_CONCURRENT 参数自己创建的 queue 时,这个方法会阻塞这个 queue(注意是阻塞 queue ,而不是阻塞当前线程),一直等到这个 queue 中排在它前面的任务都执行完成后才会开始执行自己,自己执行完毕后,再会取消阻塞,使这个 queue 中排在它后面的任务继续执行。 如果你传入的是其他的 queue, 那么它就和 dispatch_async 一样了。

func dispatch_barrier_sync(_ queue: dispatch_queue_t, _ block: dispatch_block_t):

这个方法的使用和上一个一样,传入 自定义的并发队列(DISPATCH_QUEUE_CONCURRENT),它和上一个方法一样的阻塞 queue,不同的是 这个方法还会 阻塞当前线程。 如果你传入的是其他的 queue, 那么它就和 dispatch_sync 一样了。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表