iOS多线程编程
dispatch_async(queue, ^{
NSLog(@"想执行的任务";
});
编程人员在Block语法中记述想执行的处理并将其追加到Dispatch Queue中。Dispatch Queue按照追加顺序(先进先出 FIFO,First-In-First-Out)执行处理。
// 自定义串行队列,将任务ABC加到串行队列中,顺序执行
dispatch_queue_t customSerialQueue = dispatch_queue_create("test.wangdachui.MyCustomQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(customSerialQueue, ^{
NSLog(@"customSerialQueue-A");
});
dispatch_async(customSerialQueue, ^{
NSLog(@"customSerialQueue-B");
});
dispatch_async(customSerialQueue, ^{
NSLog(@"customSerialQueue-C");
});
// 由于上面是异步执行操作,所以很难知道下面的打印和上面异步操作中的打印谁先谁后
NSLog(@"customSerialQueue-D"); //多个串行队列并行执行,系统对于一个serialQueue就只生成并使用一个线程。如果生成2000个serialQueue,那么就生成2000个线程
dispatch_queue_t customSerialQueue1 = dispatch_queue_create("test.wangdachui.MyCustomQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t customSerialQueue2 = dispatch_queue_create("test.wangdachui.MyCustomQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t customSerialQueue3 = dispatch_queue_create("test.wangdachui.MyCustomQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(customSerialQueue1, ^{
NSLog(@"customSerialQueue1-A");
});
dispatch_async(customSerialQueue2, ^{
NSLog(@"customSerialQueue2-B");
});
dispatch_async(customSerialQueue3, ^{
NSLog(@"customSerialQueue3-C");
});
//注意:过多使用多线程,就会消耗大量内存问题,引起大量的上下文切换,大幅度降低系统的响应性能
并行队列(Concurrent Dispatch Queue):
iOS和OS X的核心--XNU内核决定应当使用的线程数,并只生成所需的线程执行处理。另外,当处理结束,应当执行的处理数减少时,XNU内核会结束不再需要的线程。XNU内核仅使用Concurrent Dispatch Queue便可以完美地管理并行执行多个处理的线程。
假设准备4个Concurrent Dispatch Queue 用线程。首先blk0在线程0中开始执行,接着blk1在线程1中、blk2在线程2中、blk3在线程3中开始执行。线程0中blk0执行结束后开始执行blk4,由于线程1中blk1的执行没有结束,因此线程2中blk2执行结束后开始执行blk5,就这样循环往复。
像这样在Concurrent Dispatch Queue中执行处理时,执行顺序会根据处理内容和系统状态发生改变。
为了说明线程分配原理,这里假设线程数为4,实测iOS11线程数可达20个,所以想测试的同学,在并发队列中必须追加20个以上的任务
对于Concurrent Dispatch Queue来说,不管生成多少,由于XNU内核只使用有效管理的线程,因此不会发生串行队列的那些问题(过多使用多线程,降低系统的响应性能)
// 自定义并行队列,将任务1234567加到串行队列中
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("test.wangdachui.MyCustomQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk0");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk1");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk2");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk3");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk4");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk5");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
dispatch_async(customConcurrentQueue, ^{
NSLog(@"blk6");
NSLog(@"当前线程:%@",[NSThread currentThread]);
});
2.2.2 Dispatch Group:在使用Concurrent Dispatch Queue或同时使用多个Dispatch Queue时,想要在Dispatch Queue中的处理全部结束后再执行其他处理。可以使用Dispatch Group。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create();
// 把 queue 加入到 group
dispatch_group_async(group, queue, ^{
// 一些异步操作任务
sleep();
NSLog(@"任务GroupA\n当前线程:%@",[NSThread currentThread]);
});
// code 你可以在这里写代码做一些不必等待 group 内任务的操作
NSLog(@"任务GroupB\n当前线程:%@",[NSThread currentThread]);
// 当你在 group 的任务没有完成的情况下不能做更多的事时,阻塞当前线程等待 group 完工
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"任务GroupC\n当前线程:%@",[NSThread currentThread]);
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
// 从主线程上执行 UI 界面更新
NSLog(@"任务GroupD\n当前线程:%@",[NSThread currentThread]);
});
2.2.3 dispatch_barrier_async:
在访问数据库或文件时,为了高效地进行访问,读取处理追加到Concurrent Dispatch Queue中,写入处理在任一读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可(在写入处理结束之前,读取处理不可执行)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_async(queue, ^{
NSLog(@"blk0_for_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk1_for_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_for_writing");
});
dispatch_async(queue, ^{
NSLog(@"blk3_for_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_for_reading");
});
/*如上,如果简单地在dispatch_async函数中加入写入处理,那么根据Concurrent Dispatch Queue的性质,就有可能在追加到写入处理前面的处理中读取到与期待不符的数据,还可能因非法访问导致应用程序异常结束。因此我们要使用dispatch_barrier_async函数。dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue中。然后在由dispatch_barrier_async函数追加的处理执行完毕后,Concurrent Dispatch Queue才恢复为一般的动作,追加到该Concurrent Dispatch Queue的处理又开始执行。*/
dispatch_async(queue, ^{
NSLog(@"blk0_for_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk1_for_reading");
});
dispatch_barrier_async(queue, ^{
NSLog(@"blk2_for_writing");
});
dispatch_async(queue, ^{
NSLog(@"blk3_for_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_for_reading");
});
2.2.4 Dispatch Semaphore:信号量,关于信号量可以看我另外一篇帖子:iOS 信号量
// 创建信号量,并且设置值为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
for (int i = ; i < ; i++)
{ // 由于是异步执行的,所以每次循环Block里面的dispatch_semaphore_signal根本还没有执行就会执行dispatch_semaphore_wait,从而semaphore-1.当循环10次后,semaphore等于0,则会阻塞线程,直到执行了Block的dispatch_semaphore_signal 才会继续执行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"信号量-index=%i",i);
NSLog(@"信号量当前线程:%@",[NSThread currentThread]);
sleep();
// 每次发送信号则semaphore会+1,
dispatch_semaphore_signal(semaphore);
});
}
2.2.5 dispatch_apply:该函数按指定的次数将指定的Block追加到指定的Queue中,并等待全部处理执行结束
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_apply(, queue, ^(size_t index) {
NSLog(@"%zu",index);
});
NSLog(@"done");
/*打印结果
2017-09-20 10:49:29.760594+0800 Multithreading[11622:459025] 2
2017-09-20 10:49:29.760594+0800 Multithreading[11622:458947] 3
2017-09-20 10:49:29.760594+0800 Multithreading[11622:459027] 0
2017-09-20 10:49:29.760594+0800 Multithreading[11622:459026] 1
2017-09-20 10:49:29.760609+0800 Multithreading[11622:459077] 4
2017-09-20 10:49:29.760634+0800 Multithreading[11622:459078] 5
2017-09-20 10:49:29.760650+0800 Multithreading[11622:459079] 6
2017-09-20 10:49:29.760653+0800 Multithreading[11622:459080] 7
2017-09-20 10:49:29.760737+0800 Multithreading[11622:458947] 8
2017-09-20 10:49:29.760738+0800 Multithreading[11622:459025] 9
2017-09-20 10:49:29.761195+0800 Multithreading[11622:458947] done
*/
2.3 关于同步异步,串行并行的思考,看看打印结果,你就能悟真谛了。看的时候可以把测试代码拷贝到自己的项目中,自己思考一下,再回头看看打印结果。这样效果更好。大家不要嫌我写的烦,要好好听课。
//关于同步异步,串行,并行的思考
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_queue_t serialQueue = dispatch_queue_create("test.Lision.MyCustomQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.Lision.MyCustomQueue", DISPATCH_QUEUE_CONCURRENT); [self dispatchAsyncWith:mainQueue];
/* 打印结果
2017-09-20 19:25:40.815678+0800 Multithreading[34153:2558588] 任务C
当前线程:<NSThread: 0x604000077440>{number = 1, name = main}
2017-09-20 19:25:42.843920+0800 Multithreading[34153:2558588] 任务A
当前线程:<NSThread: 0x604000077440>{number = 1, name = main}
2017-09-20 19:25:44.845176+0800 Multithreading[34153:2558588] 任务B
当前线程:<NSThread: 0x604000077440>{number = 1, name = main}
*/ //[self dispatchAsyncWith:globalQueue];
/* 打印结果:A/B的顺序随机
2017-09-20 19:28:26.395395+0800 Multithreading[34347:2580292] 任务C
当前线程:<NSThread: 0x60400006dc80>{number = 1, name = main}
2017-09-20 19:28:28.397384+0800 Multithreading[34347:2580443] 任务B
当前线程:<NSThread: 0x604000275ac0>{number = 3, name = (null)}
2017-09-20 19:28:28.397384+0800 Multithreading[34347:2580446] 任务A
当前线程:<NSThread: 0x60c00007c680>{number = 4, name = (null)}
*/ //[self dispatchAsyncWith:serialQueue];
/* 打印结果:串行队列生成一个新线程,AB顺序执行
2017-09-20 19:29:45.542392+0800 Multithreading[34394:2585219] 任务C
当前线程:<NSThread: 0x60000007f880>{number = 1, name = main}
2017-09-20 19:29:47.547666+0800 Multithreading[34394:2585681] 任务A
当前线程:<NSThread: 0x604000277540>{number = 3, name = (null)}
2017-09-20 19:29:49.550736+0800 Multithreading[34394:2585681] 任务B
当前线程:<NSThread: 0x604000277540>{number = 3, name = (null)}
*/ //[self dispatchAsyncWith:concurrentQueue];
/* 打印结果 A/B的顺序随机,AB并行执行,生成两个线程,最多生成几个线程由系统决定
2017-09-20 19:30:56.998360+0800 Multithreading[34446:2593417] 任务C
当前线程:<NSThread: 0x60400006ba40>{number = 1, name = main}
2017-09-20 19:30:59.001208+0800 Multithreading[34446:2593777] 任务B
当前线程:<NSThread: 0x608000275ac0>{number = 3, name = (null)}
2017-09-20 19:30:59.001206+0800 Multithreading[34446:2593775] 任务A
当前线程:<NSThread: 0x60400007ae40>{number = 4, name = (null)}
*/ //[self dispatchSyncWith:mainQueue];//死锁
/*
死锁原因:我们要在主线程同步执行任务A,但是同步执行任务A也算一个任务,我们称呼它为W。mainQueue是顺序执行的,当前正在执行的任务是W,W的内容是要执行A,所以把A加到mainQueue的尾部等待执行。A要执行,必须等W完成,W要完成,必须要执行A,相互等待,进入死锁。
所以,同步的时候,不能将任务添加到当前线程的串行Queue中
*/
//[self dispatchSyncWith:globalQueue];
/* 打印结果 同步阻塞,顺序执行ABC
2017-09-20 19:59:25.961727+0800 Multithreading[34773:2655128] 任务A
当前线程:<NSThread: 0x60c000261bc0>{number = 1, name = main}
2017-09-20 19:59:27.962571+0800 Multithreading[34773:2655128] 任务B
当前线程:<NSThread: 0x60c000261bc0>{number = 1, name = main}
2017-09-20 19:59:27.962824+0800 Multithreading[34773:2655128] 任务C
当前线程:<NSThread: 0x60c000261bc0>{number = 1, name = main}
*/
// [self dispatchSyncWith:serialQueue];
/*
自己猜
*/
// [self dispatchSyncWith:concurrentQueue];
/*
自己猜
*/
} //异步调用各种Queue
- (void)dispatchAsyncWith:(dispatch_queue_t)queue {
dispatch_async(queue, ^{
sleep();
NSLog(@"任务A\n当前线程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
sleep();
NSLog(@"任务B\n当前线程:%@",[NSThread currentThread]);
});
NSLog(@"任务C\n当前线程:%@",[NSThread currentThread]);
} //同步调用各种Queue
- (void)dispatchSyncWith:(dispatch_queue_t)queue {
dispatch_sync(queue, ^{
sleep();
NSLog(@"任务A\n当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
sleep();
NSLog(@"任务B\n当前线程:%@",[NSThread currentThread]);
});
NSLog(@"任务C\n当前线程:%@",[NSThread currentThread]);
}
留了两个没有给结果,很简单的,经过上面的学习,结果是不是很简单。帖子准备了好几天,查阅了很多资料。关于使用NSOperation进行多线程编程,看我这篇帖子:iOS多线程--NSOperation
iOS多线程编程的更多相关文章
- iOS多线程编程指南
iOS多线程编程指南(拓展篇)(1) 一.Cocoa 在Cocoa上面使用多线程的指南包括以下这些: (1)不可改变的对象一般是线程安全的.一旦你创建了它们,你可以把这些对象在线程间安全的传递.另一方 ...
- iOS多线程编程原理及实践
摘要:iOS开发中,开发者不仅要做好iOS的内存管理,而且如果你的iOS涉及多线程,那你也必须了解iOS编程中对多线程的限制,iOS主线程的堆栈大小为1M,其它线程均为512KB,且这个限制开发者是无 ...
- IOS高级编程之三:IOS 多线程编程
多线程的概念在各个操作系统上都会接触到,windows.Linux.mac os等等这些常用的操作系统,都支持多线程的概念. 当然ios中也不例外,但是线程的运行节点可能是我们平常不太注意的. 例如: ...
- iOS多线程编程Part 1/3 - NSThread & Run Loop
前言 多线程的价值无需赘述,对于App性能和用户体验都有着至关重要的意义,在iOS开发中,Apple提供了不同的技术支持多线程编程,除了跨平台的pthread之外,还提供了NSThread.NSOpe ...
- iOS多线程编程指南(一)关于多线程编程(转)
原文:http://www.dreamingwish.com/article/ios-multi-threaded-programming-a-multi-threaded-programming.h ...
- iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD
原文出处: 容芳志的博客 简介iOS有三种多线程编程的技术,分别是:(一)NSThread(二)Cocoa NSOperation(三)GCD(全称:Grand Central Dispatch) 这 ...
- iOS多线程编程(四)------ GCD(Grand Central Dispatch)
一.简单介绍 是基于C语言开发的一套多线程开发机制.也是眼下苹果官方推荐的多线程开发方法.用起来也最简单.仅仅是它基于C语言开发,并不像NSOperation是面向对象的开发.而是全然面向过程的.假设 ...
- iOS多线程编程的知识梳理
多线程编程也称之为并发编程,由于其作用大,有比较多的理论知识,因此在面试中也是受到面试官的青睐.在日常项目开发中,至少网络请求上是需要使用到多线程知识的,虽然使用第三方的框架比如AFNetworkin ...
- iOS多线程编程Part 2/3 - NSOperation
多线程编程Part 1介绍了NSThread以及NSRunLoop,这篇Blog介绍另一种并发编程技术:NSOPeration. NSOperation & NSOperationQueue ...
随机推荐
- 设计模式(8)--Decorator--装饰器模式--结构型
1.模式定义: 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 2.模式特点: 装饰模式能够实现动态的为对象添加功能,是从一个对象 ...
- linux 下 Fatal error: Class ‘mysqli’ not found in
先试用这种方法 http://blog.csdn.net/u010429424/article/details/43063211 我不知道自己安装的php 没他们路径,所以用了以下这种方法处理,并且不 ...
- fodera20安装后的配置
最近安装了Fedora 20 64bit,以下是一些优化配置,使之更适合国人使用. 1,安装gnome-tweak-tool设置工具 Fedora 19自带的系统设置工具十分简单,一些重要的地方都不能 ...
- .net 正则获取url参数
public static string GetParams(string paramName) { var url = "http://fdsfs.com/Home/Index?corp= ...
- C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码
前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 本篇..基 ...
- 总结切面编程AOP的注解式开发和XML式开发
有段日子没有总结东西了,因为最近确实有点忙,一直在忙于hadoop集群的搭建,磕磕碰碰现在勉强算是能呼吸了,因为这都是在自己的PC上,资源确实有点紧张(搭建过程后期奉上),今天难得大家都有空(哈哈哈~ ...
- We’re Just Beginning
"We are reading the first verse of the first chapter of a book whose pages are infinite…" ...
- Android studio一些常见技巧(不断更新)
一.Android studio取消默认每次打开时打开最后一个项目 二.as添加jar包 新建一个libs目录,在java下 进行手动gragle同步或者如下图 三.代码边上的小图标 1.布局文件中存 ...
- 两台主机之间单向Ping不通的问题
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 } p.p2 ...
- Spark Submit 脚本
当我们需要命令行传递参数时候,将--class 写在前面,然后是jar 最后是参数 spark-submit --master yarn --num-executors 3 --executor-me ...