线程问题我会分成三篇文章来给大家做个详细的讲解
一、线程的概念
1.线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父线程;
2.线程拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父线程中的其他线程共享该进程所拥有的全部资源
3.线程是独立运行的,它并不知道进程中是否还有其他线程存在,线程的执行是抢占式的,也就是说当前的运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。
使用多线程编程的优点
1、进程间不能共享内存,但线程之间共享内存非常容易
2、系统创建进程需要重新为该进程分配资源,但是创建线程则代价小得多,因为它与父线程中的其他线程共享该进程所拥有的全部资源
3、处理异步任务的主要手段,可防止UI界面假死
线程的创建方法:
1)创建线程并执行线程
+ (void)detachNewThreadSelector: toTarget: withObject:
2)创建一个线程并返回一个线程对象但是不会执行,需要手动调用start
- (instancetype)initWithTarget: selector: object:
二、线程的状态
1、当程序创建了一个线程后,该程序就处于新建状态,此时它与其他OC对象一样,仅仅由系统为其分配了内存,并初始化了其成员变量的值,此时的线程对象没有表现任何线程的动态特征,程序也不会执行线程的线程执行体
2、当线程对象调用了start方法之后,该线程处于就绪状态,系统会为其调用方法调用栈和程序计数器,处于这种状态的线程并没有开始运行,他只是表示该线程可以运行了,至于线程何时开始运行,取决于系统的调度;倘若希望调用子线程的start方法之后子线程立即开始执行,可以在程序中加上
[NSThread sleepForTimeInterval:0.001]; //写在哪个线程中代表哪个线程
让当前运行的线程睡眠一毫秒,因为在这一毫秒内CPU不会空闲,他会去执行另一个处于就绪状态的线程,这就可以让子线程立即获得执行
三、终止子线程
线程会以以下三种方式之一结束,结束之后就处于死亡状态
1、线程执行体方法执行完成,线程正常结束
2、线程执行过程中出现了错误
3、直接调用NSThread类的exit方法来终止当前正在执行的线程
对象方法
isExecuting、isFinishing判断线程当前是否处于执行状态或者执行完成状态
如果希望在主线程中终止子线程,NSThread并没有提供方法来终止某个子线程,为了在子线程中终止子线程,可以向子线程发送一个信号(比如调用子线程的cancel方法),然后在子线程的线程执行体重进行判断,如果子线程收到过终止信号,程序应该调用NSThread类的exit方法来终止当前正在执行的循环
四、线程睡眠
如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用NSThread类的sleepXXX类方法来完成
+ (void)sleepForTimeInterval:(NSTimeInterval)ti; //每隔多久执行一次
+ (void)sleepUntilDate:(NSDate *)date; //指定睡眠指导某个时间执行
1 - (void)viewDidLoad {
2 [super viewDidLoad];
3 // NSThread 两种创建线程的方式
4 #if 1
5 //1.该方法创建的线程需要手动调用启动,才会去执行线程指定的方法
6 // 线程需要一个函数作为线程的入口函数,这个函数称为线程的入口函数
7 // 线程主要用来做并发操作,例如执行耗时的代码
8 NSThread *tread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
9 for (int i = 0; i < 100; i++) {
10 if (i == 20) {
11 //需要手动执行开始,才会调用doSomething的方法
12 [tread start];
13 [NSThread sleepForTimeInterval:0.001];
14 }
15 NSLog(@"%d",i);
16 }
17 //给线程取别名
18 //tread.name = @"something";
19
20 #elif 0
21 //2.该方式创建的线程会自动执行线程的入口函数
22 [NSThread detachNewThreadSelector:@selector(doSomething) toTarget:self withObject:nil];
23 #endif
24 }
25 -(void)doSomething
26 {
27 // 耗时代码
28
29 // 获取当前线程
30 //NSLog(@"%@",[NSThread currentThread]);
31 int index = 10;
32
33 while (index--) {
34
35 if (index == 5) {
36 //1??cancel 取消线程,并不是真正意义上的取消线程,是给线程打上取消的标志,等待取消
37 [[NSThread currentThread] cancel];
38 }
39
40 //2??.判断线程是否有取消的标志
41 if ([[NSThread currentThread] isCancelled]) {
42 //3??.退出线程,线程销毁,系统会自动回收资源
43 //[NSThread exit];
44 //注意return与退出线程的区别
45 //return;
46 }
47
48 NSLog(@"%d",index);
49 //两种睡眠方式
50 //1.睡眠
51 //[NSThread sleepForTimeInterval:1];
52 //2.指定睡眠知道某个时间执行
53 //dateByAddingTimeInterval 追加的时间
54 // NSDate *curDate = [[NSDate date] dateByAddingTimeInterval:1];
55 // [NSThread sleepUntilDate:curDate];
56 }
57 }
【注意】iOS规定只能在UI线程(即主线程)中修改UI控件的属性,因为如果程序允许任意子线程访问、修改UI控件的属性,这就需要对多个新线程的并发访问进行同步控制;否则,多个线程将会破坏UI控件内部状态的完整性
实例:下载网络图片(如果程序在UI线程中访问网络数据,由于网络速度的不确定性,当网络传输速度比较慢时,UI线程就会被阻塞,从而导致应用失去响应,因此程序将通过网络下载图片的操作放在多线程中完成)
1 #import "ViewController.h"
2
3 @int