浅谈多线程入门基础
来源:互联网 发布:excel找出a列重复数据 编辑:程序博客网 时间:2024/06/05 22:13
一. 抛砖引玉,UI中一个很常见的问题:
为什么在执行打印输出(执行耗时代码)的时候, UITextView 不能滚动? 按钮不能点击?
这是因为在同一条线程中,代码按顺序执行!所以在执行打印输出(执行耗时代码)的时候,卡住了主线程!
那么 如何解决这个问题呢?使用 NSThread类开启新线程!
1.创建一条线程;
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(longTimeOperation) object:nil];
2.启动线程; 调用start方法,告诉CPU线程准备就绪;线程被 CPU 调度之后会自动执行@selector()中的方法;
[thread start];
2. 线程
应用程序中的代码是由线程来执行的!,一个进程至少包含一条线程! 在一个应用程序启动之后,会默认开启一条线程 ----> 主线程!除了主线程之外的线程---->子线程
那么问题来了:线程是如何执行应用程序中的代码的?
(1)串行执行:
在线程中的代码是按顺序执行的!同一时间内,只能有一个代码块执行!
3. 进程
基本概念:就是在系统中'正在运行'的应用程序! 进程为应用程序开辟独立的内存空间;这块内存空间是独立的,受保护的!进程和进程之间是互不干扰的!
二.多线程执行的原理:
并发执行! 并行执行:多核CPU同时执行来.
1.问题又来了! 为什么开启一条新线程之后就能解决卡住主线程这个问题了呢?
答: 因为线程和线程之间是并发执行!
2.多线程 进程是由许多条线程组成的!
一个进程可以包含很多条线程,每条线程都可以执行不同的代码!
并发执行:
线程和线程之间是同时执行的! ----> 提高程序的运行效率!
那么问题又来了,为什么多条线程之间可以并发执行呢?
(1)线程是由 CPU 来执行的,同一时间只能有一条线程被执行!CPU在多条线程之间快速的切换!
由于 CPU 的执行速度非常快!就给我们造成了多条线程并发执行的'假象' ------- 多线程实现原理!
说到这里,大家是否会觉得多线程这么爽, 线程是不是越多越好呢?
答案是否定的,理由如下:
<1> 开启线程需要消耗一定的内存(默认情况下,线程占用 512KB 的栈区空间);
<2> 会使应用程序增加很多代码!代码变多之后,程序复杂性就会提高!
<3> CPU 在多条线程之间来回切换!线程越多, CPU就越累!
建议: 一般在实际移动应用的开发中; 只开启3~5条线程!著名的第三方框架SDWebImage默认是开启6条线程.
三.多线程很重要的一点:提高用户体验
UI线程又称作主线程,因为UIKit框架是线程不安全的,所以UI操作都要在主线程执行,尤其是在异步线程获取网络数据过后,一定要记住切换到主线程更新UI界面,否则后果非常严重.
这里可以交给大家一句话:耗时操作放子线程,更新UI放主线程.
/*----------------------------iOS中多线程实现方案 1.pthread------------------------------------*/
这里需要说明一点:(在OC中用得比较少,我在项目中几乎没有用过)
在 C 语言中的 void * 就等同于 OC 中的 id;
C语言数据类型一般以 _t/Ref 结尾
添加 pthread.h
#import <pthread.h>
/*-------------------------------------- 桥接 (__bridge) ------------------------------------*/
问题来了:为什么要使用桥接?在项目中如何使用C和OC混合开发?
桥接 (__bridge) :C 和 OC 之间传递数据的时候需要使用桥接!
1.内存管理:
在 OC 中,如果是在 ARC环境下开发,编译器在编译的时候会根据代码结构,自动为 OC 代码添加retain/release/autorelease等. ----->自动内存管理(ARC)的原理!
但是, ARC只负责 OC 部分的内存管理!不会负责 C 语言部分代码的内存管理!
也就是说!即使是在 ARC 的开发环境中!如果使用的 C 语言代码出现了 retain/copy/new/create等字样呢!我们都需要手动为其添加 release 操作!否则会出现内存泄露!
在混合开发时(C 和 OC 代码混合),C 和 OC 之间传递数据需要使用 __bridge 桥接,目的就是为了告诉编译器如何管理内存
在 MRC中不需要使用桥接! 因为都需要手动进行内存管理!
2.数据类型转换:
Foundation 和 Core Foundation框架的数据类型可以互相转换的
Foundation : OC
Core Foundation : C语言
NSString *str = @"123"; // Foundation
CFStringRef str2 = (__bridge CFStringRef)str; // Core Foundation
NSString *str3 = (__bridge NSString *)str2;
CFArrayRef ---- NSArray
CFDictionaryRef ---- NSDictionary
CFNumberRef ---- NSNumber
Core Foundation中手动创建的数据类型,都需要手动释放
CGPathRef path = CGPathCreateMutable();
CGPathRetain(path);
CGPathRelease(path);
CGPathRelease(path);
3.桥接的添加:
利用 Xcode 提示自动添加! --简单/方便/快速
/**
凡是函数名中带有create\copy\new\retain等字眼, 都应该在不需要使用这个数据的时候进行release
GCD的数据类型在ARC环境下不需要再做release
CF(Core Foundation)的数据类型在ARC\MRC环境下都需要再做release
*/
/*------------------------- iOS中多线程实现方案2.NSThread - 1基本使用 ---------------------------*/
1.三种创建线程! 2.常用方法!
{
1.NSThread: 一个 NSThread 就代表一个线程对象!
// OC语言 / 使用面向对象 / 需要手动管理线程生命周期(创建/销毁等)
2.三种多线程实现方案:
1> 先创建,后启动
// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:nil];
// 启动
[thread start];
2> 创建完自动启动
[NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:nil];
3> 隐式创建(自动启动)
[self performSelectorInBackground:@selector(download:) withObject:nil];
3.常用方法:
名字/获得主线程/获得当前线程/阻塞线程/退出线程
// 不常用: 栈区大小/优先级
1> 获得当前线程
+ (NSThread *)currentThread;
2> 获得主线程
+ (NSThread *)mainThread;
3> 睡眠(暂停)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
4> 设置线程的名字(主要用于开发中调试)
- (void)setName:(NSString *)n;
- (NSString *)name;
}
/*------------------------- iOS中多线程实现方案2.NSThread - 2线程状态 ---------------------------*/
重点:1. "Crash, P0级别 Bug!" 2.理解线程状态!
{
// 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 启动线程
[thread start];
线程池:存放线程的池子! 分为:
可调度线程池: CPU 只会调度可调度线程池中的线程! 下面蓝色状态都位于可调度线程池中! '就绪' ,'运行'!
不可调度线程池: 下面红色状态都位于不可调度线程池中! "新建" ,"阻塞" ,"死亡"!
线程状态:
start CPU调度当前线程 运行结束/强制退出(exit)
"新建" ---------->'就绪' -----------------> '运行' -----------------------> "死亡";
CPU 调度其他线程 CPU调度当前线程
'运行' ------------------> '就绪'-----------------> '运行'
调用 sleep/等待互斥锁 sleep时间到/得到互斥锁
'运行' -----------------------> "阻塞"-----------------------> '就绪';
线程运行结束或者强制退出(exit)就进入 "死亡" 状态;
"注意:一旦线程停止(死亡),就不可以再次开启任务!程序会挂掉: Crash!
重点: 平时开发中,要特别关注 Crash! :"PO"级别的 "Bug";
}
/*------------------------- iOS中多线程实现方案2.NSThread - 3资源共享 ---------------------------*/
重点:1.线程同步技术! 2.理解资源共享
{
当多条线程访问同一块资源的时候,就会出现数据错乱和数据安全的问题!
1.ATM机取钱; 卖票;
2.解决方案:互斥锁 @synchronized(锁对象self){ /*需要锁住的代码,越少越好!*/ } ------- 厕所加锁!
注意:锁定一份代码只用一把锁,用多把锁是无效的!
优点:能有效防止因多线程抢夺资源而引起的数据安全问题!
缺点:需要消耗大量的CPU资源!
结论:尽量少加锁!互斥锁的使用前提是多条线程抢夺同一块资源!
3.添加互斥锁技巧: [[NSUserDefaults standardUserDefaults] synchronize];
4.线程同步技术: ----- 互斥锁使用了线程同步技术!
多条线程在同一条线上按顺序执行任务!
5.线程安全:保证多条线程进行读写操作,都能够得到正确的结果!
用 '锁' 来实现线程安全!
}
/*--------------------------------- 原子属性和非原子属性 ---------------------------------------*/
重点:1.为什么要在主线程更新UI? 2.原子和非原子属性选择!
{
1.原子属性和非原子属性:
OC在定义属性时有 atomic 和 nonatomic 两种选择!
atomic(默认属性): 原子属性,自动为setter 方法加锁!线程安全的,需要消耗大量的 CPU 资源!
nonatomic: 非原子属性,不会为 setter 方法加锁!非线程安全的,适合内存小的移动设备!
我们在声明属性的时候该如何选择?
那么问题来了: 为什么要在主线程更新UI?
因为UIKit 框架都不是线程安全的!为了得到更好的用户体验,UIKit框架牺牲了线程安全;
所以我们要在主线程更新UI;
2.iOS 开发建议:
<1> 所有属性都声明为 nonatomic!
<2> 尽量避免多线程抢夺同一块资源!
<3> 尽量将加锁,资源抢夺等业务逻辑交给服务器端处理,减小移动客户端的压力!
}
/*------------------------- iOS中多线程实现方案2.NSThread - 4线程间通信 -------------------------*/
1.下载图片? 更新 UI?
{
1.后台线程(子线程)下载图片;
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
2.主线程更新 UI.
线程间通信常用方法:
// 最后一个参数:是否等待调用方法执行结束!
<1>[self performSelectorOnMainThread:@selector(setImageWithImage:) withObject:nil waitUntilDone:YES];
<2>[self performSelector:@selector(setImageWithImage:) onThread:[NSThread mainThread] withObject:nilwaitUntilDone:YES];
}
- 浅谈多线程入门基础
- Linux多线程基础入门
- linux多线程编程基础入门
- java基础入门----多线程1
- JAVA入门基础--浅谈枚举Enum类型
- 【SSH 基础】浅谈Hibernate--入门篇
- 【SSH 基础】浅谈Hibernate--入门篇
- 多线程浅谈
- 浅谈多线程
- 浅谈多线程
- 浅谈多线程
- 浅谈多线程
- POSIX---linux多线程编程基础入门。
- java语言基础入门——多线程
- Java多线程进阶一(基础入门)
- java多线程—基础入门实例
- Java多线程编程技术之入门基础
- 浅谈Linux下shell的入门与基础
- socket基础api
- Cisco主要产品介绍(路由器、交换机)
- sed 批量替换字符串
- Androidannotation 报错:cannot be resolved or is not a field
- 思科交换机指示灯详解
- 浅谈多线程入门基础
- NYOJ_65 另一种阶乘问题
- RIP基础理论
- OSPF基础理论
- 通过SeekBar对ImageView进行缩放,旋转
- 集成百度地图的时候的常见错误及其处理方法
- 文章标题
- leetcode 题解代码整理 26-30题
- EIGRP基础理论