多线程基础
2015-07-18 23:15
232 查看
1.多线程基础
1.1基本概念
1⃣ 进程
进程是指在系统中正在运行的一个应用程序
每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
2⃣线程
1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
3⃣多线程
1个进程中可以开启多条线程,每条线程可以并发(同时)执行不同的任务,多线程技术可以提高程序的执行效率。
1.2多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行,多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
思考:如果线程非常非常多,会发生什么情况?
CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源每条线程被调度执行的频次会降低(线程的执行效率降低)
1.3多线程的优缺点
优点:适当的提高程序的执行效率和资源利用率
缺点:开启线程需要占用一定的内存空间,如果开启大量的线程会占用大量的内存空间,降低程序的性能;线程开启的越多,cpu的地哦啊度线程的开销就越大;开启的线程多,会使程序的设计更加复杂,比如:程序间通信、多线程的数据共享……
1.4多线程在IOS开发中的应用
1.4.1 什么是主线程? 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
1.4.2 主线程的主要作用
① 显示\刷新UI界面
② 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
*1.4.3主线程的使用注意
别将比较耗时的操作放到主线程中
耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
1.5 代码
pthread
// MARK:- pthread演练
- (void)pthreadDemo {
}
void *demo(void *param) {
NSString sss = (__bridge NSString )(param);
}
NSThread创建方法
// MARK: NSThread 创建
- (void)threadDemo4 {
Person *p = [[Person alloc] init];
}
(void)threadDemo3 {
// 1
NSLog(@”1–%@”, [NSThread currentThread]);
// 是 NSObject 的一个分类方法,意味着所有的 NSObject 都可以使用此方法,在其他线程执行方法!
// 特点:没有thread字眼,一旦制定,就会立即在后台线程执行 selector 方法
// performSelectorInBackground 隐式的多线程方法
// 这种方法,在使用的时候更加灵活!
[self performSelectorInBackground:@selector(demo:) withObject:@”background”];
// 1
NSLog(@”2–%@”, [NSThread currentThread]);
}
(void)threadDemo2 {
// 1
NSLog(@”1–%@”, [NSThread currentThread]);
// detachNewThreadSelector 会理解在后台线程执行 selector 方法
// detach => 分离一个自线程执行 demo: 方法
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@”Detach”];
// 1/2?
NSLog(@”2–%@”, [NSThread currentThread]);
}
(void)threadDemo1 {
NSLog(@”1——-“);
// 实例化/加载 => alloc(分配内存) / init(初始化)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@”Thread”];
// 启动线程
[thread start];
// 1/2?
NSLog(@”2——- %@”, [NSThread currentThread]);
}
(void)demo:(id)obj {
for (int i = 0; i<2; i++) {
NSLog(@”%@ %@”, [NSThread currentThread], obj);
}
}
线程状态
(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {
NSLog(@”走!!!”);
// 一定注意,不能杀死主线程!
// [NSThread exit];
[self threadDemo];
}
(void)threadDemo {
// 实例化线程对象(新建)
NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(threadStatus) object:nil];
// 线程就绪(被添加到可调度线程池中)
[t start];
}
(void)threadStatus {
// 阻塞,当运行时满足某一个条件,会让线程”休眠”/锁
// 提示:sleep 方法是类方法,会直接休眠当前线程
NSLog(@”睡会”);
// 直接睡两秒
[NSThread sleepForTimeInterval:2.0];
for (int i = 0; i < 20; i++) {
if (i == 8) {
// 线程执行中,满足某一个条件时,再次休眠
NSLog(@”再睡会”);
// 睡到指定的日期,从现在开始过了多久
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
}
NSLog(@”能来吗?”);
}
NSThread属性
/**
优先级只是保证 CPU 调度的可能性会高!
多线程的目的:将耗时的操作放在后台,不阻塞主线程和用户的交互!
个人建议,在开发的时候,不要修改优先级!优先级翻转!
多线程开发,有一个原则:尽量简单!
在多线程开发中,不要相信一次运行的结果!
*/
(void)threadDemo {
NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
// 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
t1.name = @”Thread A”;
// 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
// 优先级只是保证 CPU 调度的可能性会高!
t1.threadPriority = 0.1;
[t1 start];
//———————-
NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
// 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
t2.name = @”Thread B”;
// 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
t2.threadPriority = 1;
[t2 start];
}
(void)demo {
for (int i = 0; i < 10; i++) {
NSLog(@”%@ %d”, [NSThread currentThread], i);
}
// // 判断是否是主线程
// if (![NSThread isMainThread]) {
// // 模拟崩溃
// NSMutableArray *array = [NSMutableArray array];
// [array addObject:nil];
// }
}
nonatomic与 atomic
nonatomic: 非原子属性
atomic: 原子属性(线程安全),就是针对多线程设计的,是默认属性
多个线程写入属性时,保证同一时间只有一个线程能够执行写入操作
单(线程)写多(线程)读的一种多线程技术,同样有可能出现“脏数据”,重新读一下。
实际上,原子属性内部也有一把锁,自旋锁
自旋锁 & 互斥锁
-共同点
都能够保证同一时间,只有一条线程执行锁定范围的代码
-不同点
互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒
自旋锁:如果发现有其他线程正在执行锁定的代码,线程会用死循环的方式,一直等待锁定代码执行完成!
自旋锁更适合执行非常短的代码!
无论什么锁,都是要付出代价的!
线程安全
在多个线程进行读写操作时,仍然能够保证数据正确!
UI 线程,共同约定:所有更新 UI 的操作都在主线程上执行!
原因:几乎所有的 UIKit 都不是线程安全的!”取舍”
日常开发中,使用锁的机会很少,多线程的目的,就是将耗时的操作放在后台!
线程间通讯
在主线程更新 UI
/**
performSelectorOnMainThread “线程间通讯”
在主线程执行的方法
传递给方法的参数
是否等待被调用方法执行完成,有可能也会等待调用方法的执行完成!几率极少!
*/
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
定时器
(void)viewDidLoad {
[super viewDidLoad];
/**
NSDefaultRunLoopMode - 时钟,网络事件;
NSRunLoopCommonModes - 用户交互;
*/
// 以下两句两种添加时钟的方法是等价的
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
// // 加入运行循环
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// scheduledTimerWithTimeInterval 默认就是使用 NSDefaultRunLoopMode
// [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
}
(void)updateTimer {
static int num = 0;
// 如果时钟触发方法,执行非常耗时的操作!注意不能在时钟调用方法中,执行耗时的操作!
NSLog(@”睡会”);
[NSThread sleepForTimeInterval:1.0];
NSLog(@”%@ %d”, [NSThread currentThread], num++);
}
1.1基本概念
1⃣ 进程
进程是指在系统中正在运行的一个应用程序
每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
2⃣线程
1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
3⃣多线程
1个进程中可以开启多条线程,每条线程可以并发(同时)执行不同的任务,多线程技术可以提高程序的执行效率。
1.2多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行,多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
思考:如果线程非常非常多,会发生什么情况?
CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源每条线程被调度执行的频次会降低(线程的执行效率降低)
1.3多线程的优缺点
优点:适当的提高程序的执行效率和资源利用率
缺点:开启线程需要占用一定的内存空间,如果开启大量的线程会占用大量的内存空间,降低程序的性能;线程开启的越多,cpu的地哦啊度线程的开销就越大;开启的线程多,会使程序的设计更加复杂,比如:程序间通信、多线程的数据共享……
1.4多线程在IOS开发中的应用
1.4.1 什么是主线程? 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
1.4.2 主线程的主要作用
① 显示\刷新UI界面
② 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
*1.4.3主线程的使用注意
别将比较耗时的操作放到主线程中
耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
1.5 代码
pthread
// MARK:- pthread演练
- (void)pthreadDemo {
/** pthread 是属于 POSIX 多线程开发框架 http://baike.baidu.com POSIX & socket 参数 1. 线程代号的地址 C语言中类型的结尾通常 _t/Ref,而且不需要使用 * 2. 线程的属性 3. 调用函数的指针 4. 传递给该函数的参数 返回值 - 如果是0,表示正确 - 如果是非0,表示错误码 void *(*)(void *) 返回值 (函数指针)(参数) void * 和 OC 中的 id 是等价的 id(*)(id) - 在 ARC 开发中,如果设计到和 C 语言中相同的数据类型进行转换时,需要使用 __bridge “桥接” - 在 MRC 开发中,不需要桥接 在 OC 中,如果是 ARC 开发,编译器会在编译的时候,自动根据代码结构,添加 retain, release, autorelease ARC 只负责 OC 部分的代码,不负责 C 的代码,如果 C 语言的框架出现 retain/create/copy 字样的函数,都需要release 关于桥接的添加,可以利用 Xcode 辅助实现! */ // 创建线程 pthread_t threadId; NSString *str = @"Hello Pthread"; int result = pthread_create(&threadId, NULL, &demo, (__bridge void *)(str)); if (result == 0) { NSLog(@"OK"); } else { NSLog(@"error %d", result); }
}
void *demo(void *param) {
NSString sss = (__bridge NSString )(param);
NSLog(@"%@, %@", [NSThread currentThread], sss); return NULL;
}
NSThread创建方法
// MARK: NSThread 创建
- (void)threadDemo4 {
Person *p = [[Person alloc] init];
[p performSelectorInBackground:@selector(loadData) withObject:nil];
}
(void)threadDemo3 {
// 1
NSLog(@”1–%@”, [NSThread currentThread]);
// 是 NSObject 的一个分类方法,意味着所有的 NSObject 都可以使用此方法,在其他线程执行方法!
// 特点:没有thread字眼,一旦制定,就会立即在后台线程执行 selector 方法
// performSelectorInBackground 隐式的多线程方法
// 这种方法,在使用的时候更加灵活!
[self performSelectorInBackground:@selector(demo:) withObject:@”background”];
// 1
NSLog(@”2–%@”, [NSThread currentThread]);
}
(void)threadDemo2 {
// 1
NSLog(@”1–%@”, [NSThread currentThread]);
// detachNewThreadSelector 会理解在后台线程执行 selector 方法
// detach => 分离一个自线程执行 demo: 方法
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@”Detach”];
// 1/2?
NSLog(@”2–%@”, [NSThread currentThread]);
}
(void)threadDemo1 {
NSLog(@”1——-“);
// 实例化/加载 => alloc(分配内存) / init(初始化)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@”Thread”];
// 启动线程
[thread start];
// 1/2?
NSLog(@”2——- %@”, [NSThread currentThread]);
}
(void)demo:(id)obj {
for (int i = 0; i<2; i++) {
NSLog(@”%@ %@”, [NSThread currentThread], obj);
}
}
线程状态
(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {
NSLog(@”走!!!”);
// 一定注意,不能杀死主线程!
// [NSThread exit];
[self threadDemo];
}
(void)threadDemo {
// 实例化线程对象(新建)
NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(threadStatus) object:nil];
// 线程就绪(被添加到可调度线程池中)
[t start];
}
(void)threadStatus {
// 阻塞,当运行时满足某一个条件,会让线程”休眠”/锁
// 提示:sleep 方法是类方法,会直接休眠当前线程
NSLog(@”睡会”);
// 直接睡两秒
[NSThread sleepForTimeInterval:2.0];
for (int i = 0; i < 20; i++) {
if (i == 8) {
// 线程执行中,满足某一个条件时,再次休眠
NSLog(@”再睡会”);
// 睡到指定的日期,从现在开始过了多久
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
NSLog(@"%@ %d", [NSThread currentThread], i); // 当线程满足某一个条件时,可以强行终止 if (i == 15) { // 一旦强行终止线程,后续的所有代码都不会被执行 // 注意:在终止线程之前,应该注意释放之前分配的对象! // 如果是 ARC 开发,需要注意,清理 C 语言框架创建的对象!否则会出现内存泄漏! [NSThread exit]; }
}
NSLog(@”能来吗?”);
}
NSThread属性
/**
优先级只是保证 CPU 调度的可能性会高!
多线程的目的:将耗时的操作放在后台,不阻塞主线程和用户的交互!
个人建议,在开发的时候,不要修改优先级!优先级翻转!
多线程开发,有一个原则:尽量简单!
在多线程开发中,不要相信一次运行的结果!
*/
(void)threadDemo {
NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
// 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
t1.name = @”Thread A”;
// 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
// 优先级只是保证 CPU 调度的可能性会高!
t1.threadPriority = 0.1;
[t1 start];
//———————-
NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
// 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
t2.name = @”Thread B”;
// 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
t2.threadPriority = 1;
[t2 start];
}
(void)demo {
for (int i = 0; i < 10; i++) {
NSLog(@”%@ %d”, [NSThread currentThread], i);
}
// // 判断是否是主线程
// if (![NSThread isMainThread]) {
// // 模拟崩溃
// NSMutableArray *array = [NSMutableArray array];
// [array addObject:nil];
// }
}
nonatomic与 atomic
nonatomic: 非原子属性
atomic: 原子属性(线程安全),就是针对多线程设计的,是默认属性
多个线程写入属性时,保证同一时间只有一个线程能够执行写入操作
单(线程)写多(线程)读的一种多线程技术,同样有可能出现“脏数据”,重新读一下。
实际上,原子属性内部也有一把锁,自旋锁
自旋锁 & 互斥锁
-共同点
都能够保证同一时间,只有一条线程执行锁定范围的代码
-不同点
互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒
自旋锁:如果发现有其他线程正在执行锁定的代码,线程会用死循环的方式,一直等待锁定代码执行完成!
自旋锁更适合执行非常短的代码!
无论什么锁,都是要付出代价的!
线程安全
在多个线程进行读写操作时,仍然能够保证数据正确!
UI 线程,共同约定:所有更新 UI 的操作都在主线程上执行!
原因:几乎所有的 UIKit 都不是线程安全的!”取舍”
日常开发中,使用锁的机会很少,多线程的目的,就是将耗时的操作放在后台!
线程间通讯
在主线程更新 UI
/**
performSelectorOnMainThread “线程间通讯”
在主线程执行的方法
传递给方法的参数
是否等待被调用方法执行完成,有可能也会等待调用方法的执行完成!几率极少!
*/
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
定时器
(void)viewDidLoad {
[super viewDidLoad];
/**
NSDefaultRunLoopMode - 时钟,网络事件;
NSRunLoopCommonModes - 用户交互;
*/
// 以下两句两种添加时钟的方法是等价的
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
// // 加入运行循环
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// scheduledTimerWithTimeInterval 默认就是使用 NSDefaultRunLoopMode
// [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
}
(void)updateTimer {
static int num = 0;
// 如果时钟触发方法,执行非常耗时的操作!注意不能在时钟调用方法中,执行耗时的操作!
NSLog(@”睡会”);
[NSThread sleepForTimeInterval:1.0];
NSLog(@”%@ %d”, [NSThread currentThread], num++);
}
相关文章推荐