符合中小企业对网站设计、功能常规化式的企业展示型网站建设
本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...
商城网站建设因基本功能的需求不同费用上面也有很大的差别...
手机微信网站开发、微信官网、微信商城网站...
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径
建网站原本是网站策划师、网络程序员、网页设计师等,应用各种网络程序开发技术和网页设计技术配合操作的协同工作。成都创新互联公司专业提供成都网站制作、成都做网站,网页设计,网站制作(企业站、自适应网站建设、电商门户网站)等服务,从网站深度策划、搜索引擎友好度优化到用户体验的提升,我们力求做到极致!
在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的。
然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务。
概要提示:
iPhone中的线程应用并不是无节制的,官方给出的资料显示,iPhone OS下的主线程的堆栈大小是1M,第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力
线程概述
有些程序是一条直线,起点到终点——如简单的hello world,运行打印完,它的生命周期便结束了,像是昙花一现。
有些程序是一个圆,不断循环直到将它切断——如操作系统,一直运行直到你关机。
一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。
Mac和IOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程。主线成在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的操作必须在主线程进行。
系统中每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则公用进程的内存空间。
每创建一个新的进成,都需要一些内存(如每个线程有自己的stack空间)和消耗一定的CPU时间。
当多个进成对同一个资源出现争夺的时候需要注意线程安全问题
创建线程
创建一个新的线程就是给进程增加一个执行流,所以新建一个线程需要提供一个函数或者方法作为线程的进口。
iOS有三种多线程编程的技术,分别是:
(一)NSThread
(二)Cocoa NSOperation
(三)GCD(全称:Grand Central Dispatch)
这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
NSThread:
优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
Cocoa NSOperation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到7了,所以不用担心该技术不能使用。
1.进程
具有一定独立功能的程序关于某次数据集合的一次运行活动,他是操作系统分配资源的基本单位。
进程是指系统正在运行的一个应用程序,就是一段程序的执行过程。
各进程之间相互独立,每个进程都运行在受保护的内存空间内,拥有独立运行所需的全部资源。
2. 线程
进程的最小执行单元。线程是一个进程的实体。
一个进程要想执行任务,就必须至少又一个线程,当程序启动时,系统默认开启一条线程,也就是主线程。
3、 进程与线程的关系
线程是进程的执行单元,进程的所有任务都在线程中执行
线程是CPU分配和执行任务的最小单元
一个程序可以有多进程,一个进程可以有多线程 ,但是一个进程至少有一个线程
同一个进程内的线程共享进程内的资源
4 多线程原理
同一时间内单核CPU 只执行一个线程,多线程是CPU 在单位时间内在多个线程之间切换,造成多个线程被同时执行的假象。
多核CPU 就可以执行多个线程
多线程的目的是同步执行多个线程,来提高运行效率
5、多线程的优缺点
有点:提高执行效率 能够提高CPU的使用率
缺点:占用一定的系统内存空间 线程越多 CPU 调度线程的开销越大 程序设计复杂(线程之间的通信 数据共享)
6、多线程并发 与并行区别
并发:在一条线程上快速切换
并行: 利用多核CPU ,多线程同时进行
7、IOS 中的多线程
NSThread 需要手动创建 不需要销毁 子线程通信很难
GCD c语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
NSOperation 基于gcd封装,更加面向对象,比gcd多了一些功能。
8、多个网络请求如何执行下一步
使用GCD的dispatch_group_t
创建一个dispatch_group_t
每次网络请求前先dispatch_group_enter,请求回调后再dispatch_group_leave,enter和leave必须配合使用,有几次enter就要有几次leave,否则group会一直存在。
当所有enter的block都leave后,会执行dispatch_group_notify的block。
NSString *str = @"";
NSURL*url = [NSURLURLWithString:str];
NSURLRequest*request = [NSURLRequestrequestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_tdownloadGroup =dispatch_group_create();
for ( int i=0; i10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask*task = [sessiondataTaskWithRequest:requestcompletionHandler:^(NSData* _Nullable data,NSURLResponse* _Nullable response,NSError* _Nullable error) {
NSLog(@"%d---%d",i,i);
dispatch_group_leave(downloadGroup);
}];
[taskresume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
7.多个网络请求顺序执行后如何执行下一步?
使用信号量semaphore
每一次遍历,都让其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),这个时候线程会等待,阻塞当前线程,直到dispatch_semaphore_signal(sem)调用之后
NSString*str=@"";
NSURL*url=[NSURL URLWithString:str];
NSURLRequest*request=[NSURLRequest requestWithURL:url];
NSURLSession*session=[NSURLSession sharedSession];
dispatch_semaphore_t sem=dispatch_semaphore_create(0);
for(inti=0;i10;i++){
NSURLSessionDataTask*task=[session dataTaskWithRequest:request completionHandler:^(NSData*_Nullable data,NSURLResponse*_Nullable response,NSError*_Nullable error){
NSLog(@"%d---%d",i,i);
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem,DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(),^{
NSLog(@"end");
});
原因有2个:
1、在子线程中是不能进行UI 更新的,而可以更新的结果只是一个幻像:因为子线程代码执行完毕了,又自动进入到了主线程,执行了子线程中的UI更新的函数栈,这中间的时间非常的短,就让大家误以为分线程可以更新UI。如果子线程一直在运行,则子线程中的UI更新的函数栈 主线程无法获知,即无法更新
2、只有极少数的UI能,因为开辟线程时会获取当前环境,如点击某个按钮,这个按钮响应的方法是开辟一个子线程,在子线程中对该按钮进行UI 更新是能及时的,如换标题,换背景图,但这没有任何意义
线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行
进程要想执行任务,必须得有线程,进程至少有一条线程
程序启动会默认开始一条线程,这条线程被称为主线程或Ui线程
进程是指在系统中正在运行的一个应用程序
每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存中
但某些时候,我们希望让主线程等待IO操作完成——例如主线程创建了一个文件夹后,等待完成,再往文件夹中创建文件。NSObject类有一个方法performSelectorOnMainThread:withObject:waitUntilDone可以让主线程挂起,但selector至多只能带一个可传参数,这时需要用到NSInvocation类来解决这个问题。代码如下:- (void)createRecordDirectoryBlockingMainThread{NSFileManager *dfm = [NSFileManager defaultManager];SEL sel = @selector(createDirectoryAtPath:withIntermediateDirectories:attributes:error:);NSMethodSignature *sig = [dfm methodSignatureForSelector:sel];//方法的签名,实际上包含了目标,选择器,及各参数的类型信息,但并不含有目标和方法的内存地址。这些信息是为了在函数压栈时准确得到各参数的值NSInvocation *ivct = [NSInvocation invocationWithMethodSignature:sig];[ivct setTarget:dfm];[ivct setSelector:sel]; //提供方法的内存地址FilePathManager *fpm = [FilePathManager sharedInstance];NSString *path = fpm.programRecordDirectoryPath;[ivct setArgument:path atIndex:2]; //参数在数组中的索引从2开始[ivct setArgument:(void *)BOOL_NO atIndex:3]; //(void *)的类型转换可以避免编译器警告[ivct setArgument:(void *)ID_NIL atIndex:4];[ivct setArgument:(void *)VOID_NULL atIndex:5];[ivct performSelectorOnMainThread:@selector(invoke)withObject:nilwaitUntilDone:YES]; //挂起主线程}
可以在主线程调用子线程之前设置一个变量保存当前数组大小,子线程开始时先将数组中指定大小的部分复制到一个临时的数组,并对这个临时数组进行操作,子线程结束时通知主线程将数组清空(清空数组的时间很短,应该不会耽误接受数据吧,如果接收数据实在频繁以至于在清空数组的过程中可能会有新到数据,那么可以维护一个数组大小变量起数组指针作用,当本需要清空数组时将改变量赋0,这样新到的数据就直接覆盖数组中指针指向的位置的值,而不必清空数组)。