一、iOS多线程

iOS多线程开发有三种方式:
  1. NSThread
  2. NSOperation
  3. GCD

iOS在每个进程启动后都会创建一个主线程更新UI要在主线程上,所以也称为UI线程,是其他线程的父线程。

线程和进程的区别傻傻分不清楚:
  • 线程(thread):用于指代独立执行的代码段。
  • 进程(process):用于指代一个正在运行的可执行程序,它可以包含多个线程。

二、NSThread

NSThreadhi轻量级的多线程开发,需要自己管理线程生命周期

创建线程主要实现方法:
/* 直接将操作添加到新线程中并执行,该方法无法拿到线程对象 */
+ (void)detachNewThreadSelector:(SEL)selector /* 方法名 */toTarget:(id)target /* 调用对象 */ withObject:(id)argument; /* 参数 */ /* 创建线程对象,初始化线程任务,调用start方法启动线程 */ - (instancetype)initWithTarget:(id)target /* 调用对象 */ selector:(SEL)selector /* 方法名 */ object:(id)argument;/* 参数 */
实际使用:
/* 创建一个线程,初始化任务,创建线程并不会启动线程 */
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage) object:nil]; [thread start];//启动线程 /* 直接将操作添加到新线程并启动线程 */ [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
  • 每个线程的实际执行顺序并不一定按启动顺序执行
  • 如果是单核CPU,多线程是并发,分时间片切换执行不同线程,多核CPU的多线程才是真正的并行运算。
线程状态分为:
  • isExecuting(正在执行)
  • isFinished(已经完成)
  • isCancellled(已经取消)
下面是常用方法来控制线程:
/* 让线程休眠 */
+ (void)sleepUntilDate:(NSDate *)date;/* 让当前执行线程休眠到某个时间 */ + (void)sleepForTimeInterval:(NSTimeInterval)time;/* 让当前执行线程休眠固定多少秒 */ /* 终止线程 */ + (void)exit; /* 停止线程,注意在主线程中调用仅仅只是设置线程状态,不会立刻停止线程 */ - (void)cancel;
实例:
NSThread *thread = threads[i];
//判断线程是否完成,如果没有完成则设置为取消状态
//注意设置为取消状态仅仅是改变了线程状态而言,并不能立刻终止线程
if ( !thread.isFinished ) { [thread cancel]; } //线程休眠2秒 [NSThread sleepForTimeInterval:2.0];

我们知道了控制单个线程,怎么在线程之间进行通信呢?

下面是线程间通信的常用方法:
/* 在后台执行一个操作,本质就是重新创建一个线程执行当前方法 */
- (void)performSelectorInBackground:(SEL)aSelectorwithObject:(id)arg;
/* 在指定的线程上执行一个方法,需要用户创建一个线程对象 */ - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; /* 在主线程上执行一个方法 */ - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

三、NSOperation

只要将NSOperation放入NSOperationQueue线程队列中,就会启动执行。
NSOperationQueue负责管理、执行所有的NSOperation,这样更加容易管理线程总数控制线程之间的依赖

NSOperation是个基类,我们直接使用的是它的子类:
  • NSInvocationOperation:调用方法SEL的方式执行线程可以使用它
  • NSBlockOperation:调用Block的方式执行线程可以使用它
下面是使用实例:
//创建Invocation线程
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImage) object:nil]; //注意如果直接调用start方法,则此操作会在主线程中调用 // [invocationOperation start];//一般不会这么操作,而是添加到NSOperationQueue中 //创建线程队列 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; operationQueue.maxConcurrentOperationCount = 5;//设置最大并发线程数 //注意添加到线程队列后,队列里的线程就会开始执行 [operationQueue addOperation:invocationOperation]; //创建Block线程 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [self loadImage:[NSNumber numberWithInt:0]]; }]; //添加进线程队列 [operationQueue addOperation:blockOperation];

如果觉得添加NSBlockOperation线程麻烦,还有个简单的方法,是NSOperationQueue的对象方法:

// 快捷添加NSBlockOperation
[operationQueue addOperationWithBlock:^{[self loadImage:[NSNumber numberWithInt:0]]; }];

我说过NSOperationQueue可以控制线程之间的依赖,这是怎么一回事呢?想象一种场景,加载多个图片,但我想最后一张图片一定要先加载,其他图片加载的前提就是最后一张图片要加载完成,这时候就可以使用依赖了。非常简单。

多图片加载实例:
- (void)loadImageWithMultiThread{int count = ROW_COUNT*COLUMN_COUNT;//创建线程队列NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init]; operationQueue.maxConcurrentOperationCount = 5;//设置最大并发线程数 //创建加载最后一张图片的线程 NSBlockOperation *lastBlockOperation = [NSBlockOperation blockOperationWithBlock:^{ [self loadImage:[NSNumber numberWithInt:(count-1)]]; }]; //创建多个线程用于下载其他图片 for (int i=0; i<count-1; ++i) { //创建多线程操作 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [self loadImage:[NSNumber numberWithInt:i]]; }]; //设置依赖操作为最后一张图片加载操作,只有最后一张图片加载完成,其他图片才开始陆续加载 [blockOperation addDependency:lastBlockOperation]; [operationQueue addOperation:blockOperation]; } //将最后一个图片的加载线程加入线程队列 [operationQueue addOperation:lastBlockOperation]; }

四、GCD

GCD中也有一个类似于NSOperationQueue的队列,GCD统一管理整个队列中的任务,GCD是C语言下的框架。

GCD中的队列分为并行队列和串行队列:
  • 串行队列(serial):只有一个线程,加入到队列中的操作按添加顺序依次执行。
  • 并发队列(concurrent):有多个线程,操作进来之后它会将这些队列安排在可用的处理器上,同时保证先进来的任务优先处理,但不是顺序的。

其实在GCD中还有一个特殊队列就是主队列,用来执行主线程上的操作任务。

GCD执行方式也分为异步执行和同步执行:
  • dispatch_async(异步执行) :不管队列中的任务执行完还是没执行完,直接将任务追加到队列
  • dispatch_sync(同步执行) : 等队列中的任务执行完,再将任务追加到队列
串行队列和并发队列的创建:
/*创建一个队列第一个参数:队列名称第二个参数:队列类型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT并发注意:GCD的queue不是指针类型
*/
dispatch_queue_t serialQueue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT); /* 使用dispatch_get_global_queue() 方法取得一个全局的并发队列 */ dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
队列的异步执行和同步执行:
//异步执行队列任务,第一个参数是队列,第二个参数是任务Block
dispatch_async(queue, ^{[self loadImage:[NSNumber numberWithInt:i]]; }); //同步执行队列任务,第一个参数是队列,第二个参数是任务Block dispatch_sync(queue, ^{ [self loadImage:[NSNumber numberWithInt:i]]; }); 
  • 在GDC中一个操作是多线程执行还是单线程执行,取决于当前队列类型和执行方法,只有队列类型为并行队列并且使用异步方法执行时才能在多个线程中并发执行
  • 串行队列可以按顺序执行,并行队列的异步方法无法确定执行顺序。
  • 更新UI界面最好采用同步方法,其他操作采用异步方法。
GCD的其他任务执行方法:
/* 重复执行某个任务,为了不阻塞线程可以使用dispatch_async()包装一下再执行 */
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));/* 单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)*/ dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); /* 常用:延迟delayInSeconds秒后在队列queue执行block操作 */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_queue_t queue, dispatch_block_t block);

五、线程同步

为什么需要线程同步?因为要解决多线程的资源抢夺的问题

1.NSLock同步锁

//初始化锁对象
NSLock *myLock = [[NSLock alloc] init];
//加锁 [myLock lock];//加锁后,下面的代码只能有一个线程进入执行 if (_imageNames.count > 0) { name = [_imageNames lastObject]; [_imageNames removeObject:name]; } //使用完解锁 [myLock unlock];

2.@synchronized代码块

//线程同步
@synchronized(self){if (_imageNames.count > 0) { name = [_imageNames lastObject]; [_imageNames removeObject:name]; } }

线程同步就不细讲了,这是一个大块知识。

转载于:https://www.cnblogs.com/ming1025/p/6064523.html

iOS学习笔记11-多线程入门相关推荐

  1. iOS学习笔记11 多线程入门

    ###一.iOS多线程 ######iOS多线程开发有三种方式: NSThread NSOperation GCD iOS在每个进程启动后都会创建一个主线程,更新UI要在主线程上,所以也称为UI线程, ...

  2. iOS学习笔记-地图MapKit入门

    代码地址如下: http://www.demodashi.com/demo/11682.html 这篇文章还是翻译自raywenderlich,用Objective-C改写了代码.没有逐字翻译,如有错 ...

  3. iOS学习笔记-105.多线程04——CGD基础

    多线程04CGD基础 一CGD简介 1 什么是GCD 2 GCD的优势 二任务和队列 1 GCD中有2个核心概念 2 GCD的使用就2个步骤 三执行任务 1 执行任务的方法 2 同步和异步的区别 3 ...

  4. iOS学习笔记-109.多线程08——CGD快速迭代

    多线程08CGD快速迭代 一说明 二for迭代 三GCD的快速迭代 四使用for迭代移动文件示例 1 说明 2 示例代码 3 图示与结果 五使用for迭代移动文件示例 1 示例代码 2 图示与结果 多 ...

  5. iOS学习笔记-110.多线程09——CGD队列组

    多线程09CGD队列组 一需求说明 二通知使用 dispatch_group_notify 1 队列组的使用可以分为四个步骤 2 示例代码 2 结果 三通知使用 dispatch_group_wait ...

  6. iOS学习笔记-108.多线程07——CGD栅栏函数、延时、一次性代码

    多线程07CGD栅栏函数延时一次性代码 一说明 1 栅栏函数说明 2 延时执行 3 一次性代码 二栅栏函数 1 说明 2 代码 3 结果 4 结果分析 三延时执行 1 调用NSObject的方法 2 ...

  7. iOS学习笔记-106.多线程05——CGD同步、异步函数和并行、串行、主队列示例

    多线程05CGD同步异步函数和并行串行主队列示例 一说明与图示 二异步函数 并发队列 1 代码 2 结果 三异步函数 串行队列 1 代码示例 2 结果 四同步函数 并发队列 1 代码示例 2 结果 五 ...

  8. 逐梦旅程学习笔记 DirectX开发入门02:旋转的彩色立方体

    本文是 系列笔记DirectX部分的第2篇,上一篇参见 逐梦旅程学习笔记 DirectX开发入门01:应用程序基本框架 这个示例增加了一些实际的内容,首先是绘制一个颜色随机变幻的彩色立方体,其二是显示 ...

  9. 点云学习笔记11——VoxelNet算法+代码运行

    点云学习笔记11--VoxelNet算法+代码运行 一.算法分析 摘要 介绍 相关工作 1.2. 贡献 2.VoxelNet 2.1.特征学习网络 2.1.1 特征学习网络 二.代码复现 2.1.环境 ...

最新文章

  1. c++入门代码_Golang Gin 实战(一)| 快速安装入门
  2. bootstrap日期时间控件
  3. Cisco QOS之CBWFQ
  4. 利用WDS实现零接触部署Windows 7 VHD
  5. 同步代码和异步代码_告别异步代码
  6. 网络爬虫--26.Scrapy中下载器中间件Downloader Middlewares的使用
  7. 学校网络安全事故救助机制初探
  8. delphi ini文件的基础读写。
  9. wps计算机打印双面输出,WPS轻松办公—-文档双面打印的两种方法
  10. pci-e串口卡linux 驱动下载,PCI/PCIe串口卡并口卡驱动
  11. 使用Python进行数独求解(二)
  12. VueI18n国际化vm._watchers[0].constructor未定义的解决办法:修改源代码吧
  13. A40I led driver
  14. 纺织erp有哪些,纺织企业为什么要上ERP
  15. android查看摄像头信息,获取Android设备上的详细的摄像头信息
  16. Mac上更新Go版本|Go Mod使用|避坑指南|
  17. 期刊论文左下角横线的添加方法
  18. Google退出Android有影响吗?
  19. python解压多层压缩包,兼容tar.gz .tgz .zip .7z .gz
  20. 集合竞价 连续竞价 开盘价如何产生

热门文章

  1. 存储类型_malloc_typedef小结
  2. 【Qt】2D绘图之绘制图片
  3. 【Linux网络编程】浅谈 C/S 和 B/S 架构
  4. 【Android】Android中屏蔽返回键,home键以及其他实体按键
  5. mysql union 放弃索引_MySQL的or/in/union与索引优化
  6. linux mariadb 乱码,配置mariadb远程访问权限,解决数据库乱码问题
  7. python删除链表满足pred的元素_python 数据结构一 之 线性表
  8. Js中 call() 与 apply() exports
  9. 缺失的第一个正数—leetcode41
  10. 使用wcstok_s函数分割路径, 创建多级目录