iOS基础 - 线程 1.1:简介 | NSThread
概念
1 - 程序:由代码生成的可执行应用(例如:QQ.app)
2 - 进程:一个正在运行的程序可以看做是一个进程,它拥有独立运行所需要的全部资源
3 - 线程:程序中独立运行的代码段
4 - 主线程:一个正在运行的程序(即进程),至少包含一个线程,即主线程。主线程在程序启动时被创建,用于执 main 函数
5 - 单线程程序:只有一个主线程的程序
6 - 多单线程程序:拥有多个线程的程序
一个进程行由一个或多个线程组成。进程只负责资源的调度和分配,线程才是程序执行的真正单元,负责代码的执行
主线程负责执行程序的所有代码(UI 展示刷新、本地存储、网络请求等等),这些代码只能顺序执行,无法并发执行
子线程和主线程都是独立运行的单元,各自执行互不影响,因此能够实现并发执行
7 - iOS UI 的添加和刷新必须在主线程中操作
并发 | 并行
1 - 并发是指一个处理器同时处理多个任务(并非真正意义上的同时,任务超速切换)
2 - 并行是指多个处理器或者是多核的处理器同时处理多个不同的任务
3 - 并发是逻辑上的同时发生,而并行是物理上的同时发生
打个比喻,并发是指同一个人同时吃三个馒头(并不是真正意义上的同时);并行是指三个人同时吃三个馒头,人手一个(真正意义上的同时)
当有多个线程在操作时,如果系统只有一个 CPU,则它根本不可能真正同时进行一个以上的线程
4 - 并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在
5 - 并发能够在单处理器系统中存在是因为并发是并行的假象
并行则不同,它要求程序能够同时执行多个操作
NSThread
1 - 它是一个轻量级的多线程,一个 NSThread 对象就代表一条线程
2 - 常用 API
3 - 代码示例:使用 NSThread 创建线程
1 // 获取当前线程 2 NSThread *current = [NSThread currentThread]; 3 NSLog(@"%@",current);// 主线程 4 5 // 获取主线程 6 NSThread *main = [NSThread mainThread]; 7 NSLog(@"%@",main);// 主线程 8 9 // 开启子线程:执行一些耗时操作 10 [self creatNSThread01];
1 // 开启子线程: 2 -(void)creatNSThread01{ 3 4 // 方式一 5 // 创建一个子线程 6 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"线程A"]; 7 // 为线程设置一个名称 8 thread.name = @"线程A"; 9 // 开启子线程 10 [thread start]; 11 12 // 同样,再次创建一个子线程 13 NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"线程B"]; 14 thread2.name=@"线程B"; 15 [thread2 start]; 16 17 18 // 方式二 19 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"创建完线程直接启动,不需要手动开启"]; 20 21 // 方式三:隐式创建线程,并且直接启动 22 [self performSelectorInBackground:@selector(run:) withObject:@"隐式创建"]; 23 24 }
1 -(void)run:(NSString *)str{ 2 3 NSThread *current = [NSThread currentThread]; 4 for (int i = 0; i < 5; i++) { 5 // 三个子线程轮流切换输出 6 NSLog(@"run---%@---%@",current,str); 7 } 8 }
日志打印
4 - NSThread 其它常用 API
1 - (BOOL)isMainThread; // 是否为主线程 2 + (BOOL)isMainThread; // 是否为主线程 3 4 // 线程的调度优先级:调度优先级的取值范围是 0.0 ~ 1.0,默认 0.5,值越大,优先级越高 5 + (double)threadPriority; 6 + (BOOL)setThreadPriority:(double)p; 7 8 // 设置线程的名字 9 - (void)setName:(NSString *)n; 10 - (NSString *)name;
5 - 代码示例:验证线程执行环境(主线程遍历会阻塞八卦图旋转;子线程遍历则与八卦图旋转同时进行)
1 #import "ViewController.h" 2 #define MainScreen_W [UIScreen mainScreen].bounds.size.width 3 @interface ViewController () 4 5 @property(strong,nonatomic)UIImageView *gossipIV;// 八卦图 6 7 @end 8 9 @implementation ViewController 10 11 - (void)viewDidLoad { 12 [super viewDidLoad]; 13 14 self.gossipIV = [[UIImageView alloc] initWithFrame:CGRectMake((MainScreen_W-220)/2.0, 80, 220, 220)]; 15 _gossipIV.image = [UIImage imageNamed:@"gossip.jpg"]; 16 [self.view addSubview:_gossipIV]; 17 18 UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 19 [btn setTitle:@"阻塞" forState:UIControlStateNormal]; 20 btn.backgroundColor = [UIColor redColor]; 21 btn.frame = CGRectMake(40, 400, MainScreen_W-80, 40); 22 [btn addTarget:self action:@selector(chokeIV) forControlEvents:UIControlEventTouchUpInside]; 23 [self.view addSubview:btn]; 24 25 UIButton *btnB = [UIButton buttonWithType:UIButtonTypeCustom]; 26 [btnB setTitle:@"子线程" forState:UIControlStateNormal]; 27 btnB.backgroundColor = [UIColor redColor]; 28 btnB.frame = CGRectMake(100, 500, MainScreen_W-200, 40); 29 [btnB addTarget:self action:@selector(noChokeIV) forControlEvents:UIControlEventTouchUpInside]; 30 [self.view addSubview:btnB]; 31 32 [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(ratateImageView) userInfo:nil repeats:YES];// 定时旋转,用来查看线程阻塞状况 33 } 34 35 // 旋转的八卦图 36 - (void)ratateImageView{ 37 38 self.gossipIV.transform = CGAffineTransformRotate(self.gossipIV.transform, M_PI_4/4); 39 } 40 41 // 默认主线程:遍历完成之前,八卦图被阻塞(静止不动) 42 -(void)chokeIV{ 43 44 for (int i = 0; i < 5; i++) { 45 [NSThread sleepForTimeInterval:0.5];// 线程休眠 46 NSString *str = [NSString stringWithFormat:@"i = %d",i]; 47 NSLog(@"%@", str);// 线程休眠后,0.5秒一输出 48 } 49 } 50 51 // 使用子线程:八卦图继续旋转,遍历同样正常打印 52 -(void)noChokeIV{ 53 54 [NSThread detachNewThreadSelector:@selector(test01) toTarget:self withObject:nil]; 55 } 56 57 -(void)test01{ 58 59 /* MRC 模式下,子线程中处理的操作需要自己加自动释放池 60 61 @autoreleasepool { 62 63 for (int i = 0; i < 5; i++) { 64 [NSThread sleepForTimeInterval:0.5];// 线程休眠 65 NSString *str = [NSString stringWithFormat:@"i = %d",i]; 66 NSLog(@"%@", str);// 线程休眠后,0.5秒一输出 67 } 68 } 69 */ 70 71 for (int i = 0; i < 10; i++) { 72 [NSThread sleepForTimeInterval:0.5];// 线程休眠 73 NSString *str = [NSString stringWithFormat:@"i = %d",i]; 74 NSLog(@"%@", str);// 线程休眠后,0.5秒一输出 75 } 76 77 // 子线程不做刷新 UI 的事情,因为可能会导致刷新失败 78 [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES];// 等待遍历完成,才会执行此行代码 79 /* 在子线程中,NSTimer 是默认不能使用。如果想要使用,则需要配合开启循环事件 80 81 [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(ratateImageView) userInfo:nil repeats:YES];// 不开启,不触发 82 [[NSRunLoop currentRunLoop] run];// 开启事件循环 83 */ 84 } 85 86 // 更新 UI 87 -(void)updateUI{ 88 89 self.view.backgroundColor = [UIColor cyanColor]; 90 } 91 92 @end
运行效果
素材链接
https://pan.baidu.com/s/1kDEpvujHJTRoTm-DrBtYSA
kn07