多线程
2016-05-21 本文已影响120人
没有名字就是我的名字
内存耗时问题
1. 整型常量放在栈区读写速度最快
2. 字符串存在常量区读写速度有点慢
3. NSString stringWithFormat存在堆区,读写速度较慢
4. I/O操作读写速度非常慢
5. 网络操作最慢
6. 循环不消耗内存,消耗Cpu
线程是进程的基本执行单元
多线程的优缺点
优点:
1. 能适当的提高程序的执行效率
2. 能适当的提高资源的使用率
3. 线程执行完成会自动销毁
缺点:
1. 开启线程需要占用一定的内存空间(默认情况下,每一条线程都占用512KB),如果开启大量的线程,会占用大量的内存空间,从而降低程序的性能。
2. 线程越多,CPU在调度线程上的开销就越大。
3. 线程越多,程序设计就会更复杂:比如线程间通讯、多线程的数据共享等。
多线程的作用:
1. 解决程序阻塞问题;
2. 提高程序的运行效率
多线程中同步和异步的概念
1. 同步:类似于写代码一样,从上往下执行,在生活中一个人按照先后顺序做某一件事情.
2. 异步:多个人同时做一件事情.
单核的Cpu执行原理
1. 同一时间,Cpu只能执行一个线程。
2. 多线程同时执行,是Cpu快速的在多个线程之间的切换。
3. Cpu调度线程的速度足够快,就造成了多个线程的"同时"执行。
4. 如果线程数非常多,Cpu就会在n个线程之间切换,消耗大量的Cpu资源。
运行循环(消息循环)
1、只有主线程的运行循环默认是开启的,其它子线程的运行循环都是默认关闭的。
2、运行循环是一个死循环,只有满足一定的条件才会退出。
让运行循环退出的方式:
runUntilDate
运行循环的作用:
1、保证程序一直在运行,程序之所以是一直在运行是因为运行循环是一个死循环。
2、如果没有输入事件(输入源和时间源),运行循环会进入休眠模式。
运行循环的模式
Default、Connection、Modal、Normal、Eventtracking、Common models
关于运行循环的执行
运行循环是在一个指定的模式下运行的,输入事件也有对应的模式,只有两个模式相互匹配的时候,运行循环才会执行。
没有输入源运行循环是不会执行!
当运行循环启动后,默认的模式为DefaultModel。
关于子线程的运行循环步骤
1、创建子线程:
NSTread *thread = [ [NSThread alloc ] initWithTarget:self selector:@selector(test) object:nil];
//开启线程
[thread start];
2、在子线程的运行循环中添加输入源,否则运行循环是不会执行的。
[self performSelector:@selector(test1) onThread :thread withObject:nil waitUntileDone:NO];
//然后在自定义的方法中实现根据需求实现的功能就OK
GCD的核心概念
//将任务添加到queue,并且指定根据需要要执行的函数,GCD的关键概念简单的概括为:
1、任务:你需要让控制器帮你做什么事情,就把对应的代码写在这里。
2、队列:需要把你要执行的任务添加到queue(队列)中,就是用来存放任务。
队列分为:串行队列、并发队列、主队列、全局队列--全局队列一直存在,需要的时候直接获取全局队列即可。
3、执行任务的函数:
(1)、同步 dispatch_sync
(2)、异步 dispatch_async
异步加并发会开启新的线程!
GCD的使用步骤
第一步:创建或者获取queue;
第二步:创建任务。
第三步:把任务添加到queue;
注意:要遵循队列的FIFO原则:先进先出,后进后出!
代码展示
//获取全局队列
dispatch_queue_t que = dispatch_queue_create(0, 0);
//创建任务
dispatch_block_t test = ^{
NSLog(@"快执行我!");
};
//添加到队列
dispatch_sync(que, test);
简化后的代码
-(void)test{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"快执行我");
});
}
总结:使用GCD不需要管理线程的创建、释放和复用,我们不用管理线程的运行周期。
串行队列
1、dispatch_queue_t queue=dispatch_queue_create("WFT", DISPATCH_QUEUE_SERIAL);
2、dispatch_queue_t queue=dispatch_queue_create("WFT", NULL);
//特点:
1、以先进先出的方式,按顺序调度队列中的任务去执行,并且一次只能调度一个任务。
2、无论队列中的任务是同步的还是异步,在执行的时候都必须等待前一个任务执行完成之后才执行后面的任务。
并发队列
dispatch_queue_t queue=dispatch_queue_create("WFT", DISPATCH_QUEUE_CONCURRENT);
主队列:
主队列是系统提供的,我们不用自己创建主队列,需要用的时候直接通过Dispatch_get_main_queue( )直接获取就可以!
主队列的特点:
1、添加到主队列的任务只能让主线程去执行。
2、依然遵循FIFO先进先出的原则,只有当主线程的代码执行完毕之后,主队列的任务才会执行。
3、主队列就相当于一个全局串行队列。
4、主队列和串行队列的区别:
① 串行队列:必须等待一个任务执行完毕,才会调度下一个任务。
② 主队列:如果主线程上有代码执行,主队列就不调度任务。
死锁的问题的原因:
主线程和主队列同时遵循先进先出,同步执行的原则,所以会造成相互等待,从而死锁。
解决办法:在外部加一个异步执行就ok!也就是说把主队列同步执行的任务放到子线程去执行。
原因:主队列同步任务放到了子线程中去执行,主队列同步任务无法阻塞主线程执行的代码,因此主线程可以讲主线程上的代码执行完毕。当主线程执行完毕之后,就会执行主队列里面的任务。
全局队列
全局队列也是系统创建的,无需自己去创建,可以直接通过dispatch_get_global _queue(long identifier, unsigned long flags)函数来获取。
全局队列的特点跟着并发队列一致。其实,全局队列就是系统提供的一种特殊的并发队列。
全局队列和并发队列的区别:
① 全局队列没有名称、并发队列有名称。
② 在全局队列中无论是arc还是mrc都不需要考虑内存的释放,但是并发队列在mrc中需要使用dispatch_release来释放相应的对象。(在日常开发的过程中,建议使用全局队列)
总结:
关于开不开线程的问题:
1、同步执行的时候不开启线程。
2、异步执行的时候开启线程。
3、异步执行任务在串行队列中只会开一条线程。
4、异步执行任务在并发队列中开启线程的数量是根据线程池决定的。
注意:对于主线程来说,不管是同步执行还是异步执行都不会开启线程。
延迟执行和一次执行
延迟执行代码:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1.0*NSEC_OER_SEC)),dispatch_get_main_queue(),^{
//做你想做的事情
});
一次执行代码
static dispatch_once_tonceToken;
NSLog(@"%zd", onceToken);
for(int i = 0; i < 10; i++) {
dispatch_once(&onceToken, ^{
NSLog(@"我就执行一次!");
});
}