多线程网络的学习
什么是进程?
系统中正在运行的一个应用程序是一个进程。
比如同时打开qq xcode系统就会分别启动两个进程。而且是相对独立的进程,相互不影响。
1个进程想要执行任务,就必须有线程,每个进程至少有1个线程。
线程是进程的基本执行单元,一个进程的所有任务都在线程中执行。
串行:
1个线程中的任务是串行(顺序执行)的。
如果在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。
也就是说同一时间内,1个线程只能执行1个任务。
比如下载任务。
多线程
1个进程中可以开启多个线程,每个线程可以并发(同时)执行不同任务。
例如:电脑可以打开扣扣聊天,可同时听音乐,浏览网页等。
多线程原理:
同一时间,cpu只能处理1条线程,只有一个线程在工作(执行)。
线程并发执行,其实就是cpu快速地在多条线程之间切换。——-也就是每个线程每次都会执行一点,直至所有进程都执行完毕。
若果cpu调度线程地时间足够快,就造成了多线程地并发执行地假象。
思考:如果线程非常多,会发生什么情况?
Cpu会在n条线程直接切换,cpu会累死,消耗大量地cpu资源
多线程地优点
1.提高程序执行效率
2.能适当提高资源利用率
多线程缺点
1.开启线程需要占用一定地内存空间(默认情况下 主线程占用1m 子线程占用512mb)如果开启大量线程,会占用大量地内存空间,降低程序地性能。
2.线程越多,cpu在调度线程上地开销就越大。
3.程序设计更加复杂:比如线程之间地通信、多线程地数据共享。
什么是主线程?
一个ios程序运行后默认会开启1个线程,成为主线程或ui线程。跟ui相关地操作都放在主线程。
主线程地主要作用:
1.显示/刷新ui界面。
2.处理ui事件(点击、滚动、拖拽)
主线程使用注意:
别讲比较耗时地操作放到主线程中(比如请求网络这个操作就不要放在主线程中)
耗时操作,会卡住主线程,严重影响ui流畅度,给用户一种“卡”地坏体验。
打印出当前所在地线程 number == 1 主线程 number!= 1 子线程 次线程 其他线程。
NSLog(@”%@”,[NSThread currentThread]);
将耗时操作放到子线程中去,也就是为耗时操作开辟一个新的线程空间让其在里边运行。 LongTime是方法名称,它就是在子线程中运行地。 Withobject是参数。
[self performSelectorInBackground:@selector(longTime) withObject:nil];
ios中地多线程实现方案:
1. pthread :————程序员管理,几乎不用它是底层地 c语言
- 一套通用地多线程api
- 适用于unix/linux/win等系统
- 跨平台/可移植
- 使用难度大
2. NSThread 程序员管理 偶尔使用 oc
- 使用更加面向对象
- 更简单宜用,直接操作线程对象
3.GCD 自动管理 经常使用 c语言
- 旨在代替NSThread等线程技术
- 充分利用设备地多核
4.NSOperation 自动管理 经常使用 oc语言
- 基于GCD(底层是GCD)
- 比GCD多了些简单实用功能
- 使用更加面向对象
使用pthread必须引入 import<pthread.h>这个头文件。
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
[self text1];
}
// void * 必须return -void *run(void *param) { //耗时操作在这里执行。 for(int i=0; i< 200000; i ++) { NSLog(@"%@",[NSthread currentThread]); } return NULL; } -(void)test1 { pthread_t threadId; //参数: 1.要开的线程的变量 2.线程的属性 3.要在开辟的子线程要执行的函数(函数) 4.这个字线程任务需要传递的参数。 pthread_create(&threadId,NULL,run,NULL); }
void* 相当于oc 语言中的id 可以指向任意变量/对象。
NSThread的使用方法:
一个NSThread就代表一个线程。
c
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { [self text1]; } -(void)run { for(int i = 0;i<10;i++) { NSLog(@"%@",[NSThread currentThread]); } } // 创建线程方式 1. -(void)test1 { // 实例化一个线程对象 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; // 让线程开始工作,启动线程,在新开的线程中,执行run方法。 [thread start]; } -(void)run2:(NSString *)str { for(int i = 0;i<10;i++) { NSLog(@"%@",[NSThread currentThread]); } } // 创建线程方式2. -(void)test2 { [ NSTread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"hello"]; } // 创建线程方式3 -('void)test3 { // “隐式”创建线程的方式。 它没有明确说出创建线程,而其实质就是创建新的线程。 // 其余两种都是显式的创建线程。 [self performSelectorInBackground:@selector(run:) withObject:"hello"]; }
-(void)test4
{
// 实例化一个线程对象
NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
threadA.name = @"thread A"; // 线程的名称。
// 当前线程的优先级。一般情况下不会去设置 ,浮点型的取值,取值范围0.0~1.0 由低到高 ,默认是0.5
threadA.threadPriority = 0.1;
// 让线程开始工作,启动线程,在新开的线程中,执行run方法。
[threadA start];
NSThread *threadB = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; threadB.name = @"thread B"; // 线程的名称。
// 当前线程的优先级。一般情况下不会去设置 ,浮点型的取值,取值范围0.0~1.0 ,默认是0.5
threadB.threadPriority = 1.0;
// 让线程开始工作,启动线程,在新开的线程中,执行run方法。 [threadB start];
}
主线程的相关方法:
+(NSThread *)mainThread; // 获得主线程
-(BOOL)isMainThread; // 是否为主线程
+(BOOL)isMainThread; // 是否为主线程(类方法)
线程的状态:
1.新建状态 (New)
NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
2.就绪状态(Runnable)
[thread start]; // 这个时候cpu就可以调度当前线程了----当前线程被添加到可调用线程池中。--cpu 只会在这个池中调用线程。
3.运行状态(Runing)
cpu调用当前线程的时候当前线程就是-运行状态,如果cpu调用其他线程,那么当前线程就又返回成“就绪状态”;
4.阻塞状态(Blocked)
调用了sleep方法等待同步锁
变成阻塞状态之后,当前线程就会从可调用线程池中被移除。cpu就找不到当前线程。
当sleep到时得到同步锁,当前线程会重新添加到可调用线程池中。
在可调用线程池中线程只存在着两种状态:就绪---运行
5.Dead
线程任务执行完毕异常强制退出
从可调用线程池中移除,然后从内存中销毁。-----一旦死亡,就不能再次开启任务。
启动线程
-(void)start // 进入就绪状态->运行状态。当线程任务执行完毕,自动进入死亡状态。
阻塞(暂停)线程
+(void)sleepUntilDate:(NSDate *)date; // 睡到什么时候。
+(void)sleepForTimeInterval:(NSTimeInterval)time;// 进入阻塞状态 睡多长时间
+(void)exit; // 进入死亡状态 。
多线程存在着安全隐患,因为可能会出现多个线程同时访问同一块资源,出现资源错乱等现象。
eg:银行存款,可能会出现同一账号同时存取的情况。
这种情况下利用互斥锁(独占锁)进行可以解决当前问题,而互斥锁需要锁住执行状态,也就是关键代码只可一个线程访问。
@property (nonatomic ,strong)NSInteger tickets; -(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { [self text1]; } // void * 必须return -void *run(void *param) { while(YES) { [NSThread sleepForTimeInterval:1.0];
// 确保synchronized的参数在多个线程访问的时候都是同一个对象。也就是说这个锁都是同一把锁。 @synchronized(self){ if(self. tickets>0) { self.tickets--; }else{NSLog(@"票售完了");break;} }; } return NULL; } -(void)test1 { NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; threadA name = @"售票员a" [threadA start]; NSThread *threadB = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; threadB.name = @"售票员b" [threadB start]; }
互斥锁锁定的代码一定要少。加锁范围内的代码同一时间只允许一个线程访问,互斥锁的参数:任何继承NSObject *对象都可以。要保证这个锁,所有的线程都能访问到。由于锁的性能太差,苹果官方不推荐使用。(消耗大量的cpu资源)而在ios开发中我们做的都是前端,所以不需要关心加锁问题,这个问题时由服务器进行解决的。