目录
一、多线程简介
1、多线程的由来
2、耗时操作的模拟试验
3、进程和线程
4、多线程的概念及原理
5、多线程的优缺点和一个Tip
6、主线程
7、技术方案
二、Pthread
---
1、函数
2、参数和返回值
3、使用
三、NSThread
---
1、创建一个新的线程
2、线程的状态
3、线程的属性
四、互斥锁
---
1、访问共享资源引入问题!
2、互斥锁介绍
3、互斥锁原理
4、互斥锁和自旋锁
五、GCD
---
1、GCD介绍
2、GCD的两个核心
3、函数
4、串行队列和并发队列
5、主队列
6、全局队列
7、GCD总结
六、NSOperation
---
1、NSOperation简介
2、核心概念
3、操作步骤
4、NSInvocationOperation
5、NSBlockOperation
七、案例
---
***
一、多线程简介
1、多线程的由来
一个进程(进程)在执行一个线程(线程中有很多函数或方法(后面简称Function))的时候,其中有一个Function执行的时候需要消耗一些时间,但是这个线程又必须同时执行这个Function之后的Function,问题来了,一个线程中的任何一个Function都必须等待其执行完成后才能执行后面的Function,如果要同时执行两个或者多个Function,那么,就必须多开一个或者多个线程,这就是多线程的产生。我想多线程最开始的诞生就是由这而来吧!
2、耗时操作的模拟试验
2.1 循环测试
代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
}
NSLog(@"end");
}
return 0;
}
控制台
2016-02-16 13:51:54.140 Test[1670:603696] bengin
2016-02-16 13:51:54.160 Test[1670:603696] end
Program ended with exit code: 0
结论一:循环一亿次耗时0.02秒,计算机的运行速度是非常快的
2.2 操作栈区
代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
int n = 1;
}
NSLog(@"end");
}
return 0;
}
控制台
2016-02-16 13:57:37.589 Test[1734:631377] bengin
2016-02-16 13:57:37.612 Test[1734:631377] end
Program ended with exit code: 0
结论二:对栈区操作一亿次,耗时0.023秒
2.3 操作常量区
代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
NSString *str = @"hellow";
}
NSLog(@"end");
}
return 0;
}
控制台
2016-02-16 14:03:59.003 Test[1763:659287] bengin
2016-02-16 14:03:59.113 Test[1763:659287] end
Program ended with exit code: 0
结论三:对常量区操作一亿次,耗时0.11秒
2.4 操作堆区
代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
NSString *str = [NSString stringWithFormat:@"%d",i];
}
NSLog(@"end");
}
return 0;
}
控制台
2016-02-16 14:09:03.673 Test[1786:673719] bengin
2016-02-16 14:09:10.705 Test[1786:673719] end
Program ended with exit code: 0
结论四:对堆区操作一亿次耗时7秒多一些,较慢!
2.5 I/O操作
代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
NSLog(@"%d",i);
}
NSLog(@"end");
}
return 0;
}
控制台输出!正在跑中,一亿次!!!先看截图
CPU
再看内存
好吧,还在跑,现在已经达到10分钟了,怕心疼本本炸掉!stop。。。结论五:I/O操作非常慢,一亿次10分钟也没能跑完!
最终结论:通过以上结论一、二、三、四、五得出一个结论,各个区的执行效率:栈区>常量区>堆区>I/O操作。同时也说明了一个问题,执行不同的方法会产什么耗时操作。这是,为了解决耗时操作问题,多线程闪亮诞生!
3、进程和线程
先说说进程和线程吧!
3.1 进程
3.1.1 进程的概念:系统中正在运行的应用程序。
3.1.2 进程的特点:每个进程都运行在其专用且受保护的内存空间,不同的进程之间相互独立,互不干扰。
3.2 线程
3.2.1 线程的概念:线程是进程的执行任务的基本单元,一个进程的所有任务都是在线程中执行的。(每一个进程至少要有一条线程)。
3.2.2 线程的特点:线程在执行任务的时候是按顺序执行的。如果要让一条线程执行多个任务,那么只能一个一个地并且按顺序执行这些任务。也就是说,在同一时间,一条线程只能执行一个任务。
我们可以通过Mac中的活动监视器查看进程和线程,下图!
4、多线程的概念及原理
4.1 多线程概念:1个进程可以开启多条线程,多条线程可以同时执行不同的任务。
4.2 多线程原理:
前提是在单核CPU的情况下,同一时间,CPU只能处理一条线程,也就是说只有一条线程在执行任务。多线程同时执行,那是不可能的!但是是CPU快速地在多条线程之间进行调度和切换执行任务。如果CPU调度线程的速度足够快,就会造成多条线程同时执行任务的”假象”,这种假象,就被美誉为:多线程!
5、多线程的优缺点和一个Tip
5.1 多线程的优点
可以适当提高程序的执行效率-
也可以适当提高资源的利用率(CPU、内存利用率)5.2 多线程的缺点
开启一条线程需要占用一定的内存空间(默认情况下,每一条线程都占用512KB),如果开启大量的线程,会占用大量的内存空间,从而降低程序的性能。线程越多,CPU在调度和切换线程上的开销就会越大。-
线程数越多,程序的设计会越复杂。5.3 Tip
-
开启新的线程就会消耗资源,但是却可以提高用户体验。在保证良好的用户体验的前提下,可以适当地开线程,一般开3-6条。
-
开启一条新的线程,默认情况下,一条线程都是占用512KB,但是官方的文档里面给出的说明却不是,为了得出真相,下面做个小小的测试!
代码int main(int argc, const char * argv[]) {
@autoreleasepool {
/** 操作主线程 /
NSLog(@"主线程默认 %tu", [NSThread currentThread].stackSize / 1024);
// 设置主线程的stackSize
[NSThread currentThread].stackSize = 1024 1024;
NSLog(@"主线程修改 %tu", [NSThread currentThread].stackSize / 1024);/** 操作子线程 */ NSThread *thread = [[NSThread alloc] init]; NSLog(@"thread默认 %tu", thread.stackSize / 1024); // 设置子线程的stackSize thread.stackSize = 8 * 1024; NSLog(@"thread修改 %tu", thread.stackSize / 1024); [thread start];}
return 0;
}
控制台2016-02-17 08:36:02.652 Test[609:110129] 主线程默认 512
2016-02-17 08:36:02.654 Test[609:110129] 主线程修改 1024
2016-02-17 08:36:02.654 Test[609:110129] thread默认 512
2016-02-17 08:36:02.654 Test[609:110129] thread修改 8
结论七:证明了,不管什么线程,默认都是512,最小为8.可能是官方文档没有及时更新吧!
6、主线程
6.1 主线程的概念:
一个应用程序在启动运行后,系统会自动开启1条线程,这条称为”主线程”。
6.2 主线程的作用:主线程的作用主要用于处理UI界面刷新和UI时间!
6.3 结论:主线程上不能执行耗时操作,这样会造成界面卡顿,给用户一种不好的体验。
7、技术方案
***
二、Pthread
1、函数
pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict)
2、参数和返回值
- pthread_t *restrict 线程编号的地址
- const pthread_attr_t *restrict 线程的属性
- void *(*)(void *) 线程要执行的函数void * (*) (void *)
- int * 指向int类型的指针 void * 指向任何类型的指针 有点类似OC中的id
-
void *restrict 要执行的函数的参数
-
返回值 int类型 0是成功 非0 是失败
3、使用
代码
#import <Foundation/Foundation.h>
#import <pthread/pthread.h>
void *demo(void *param) {
NSString *name = (__bridge NSString *)(param);
NSLog(@"hello %@ %@",name,[NSThread currentThread]);
return NULL;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
//创建子线程
pthread_t pthread; //线程编号
NSString *test = @"test";
int result = pthread_create(&pthread, NULL, demo, (__bridge void *)(test));
NSLog(@"Began %@",[NSThread currentThread]);
if (result == 0) {
NSLog(@"成功");
}else {
NSLog(@"失败");
}
}
return 0;
}
控制台
2016-02-16 22:00:57.401 Test[888:42585] Began <NSThread: x100502d70>{number = 1, name = main}
2016-02-16 22:00:57.403 Test[888:42615] hello test <NSThread: x100102a30>{number = 2, name = (null)}
2016-02-16 22:00:57.403 Test[888:42585] 成功
-
__bridge 桥接,把OC中的对象,传递给c语言的函数,使用__bridge
***三、NSThread
1、创建一个新的线程
-
方式一
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil]; -
方式二
[NSThread detachNewThreadSelector:@selector(demo) toTarget:self withObject:nil]; -
方式三
[self performSelectorInBackground:@selector(demo) withObject:nil];
2、线程的状态
线程状态分为五种
- 创建 New
- 就绪 Runnable
-
运行 Running
- (void)start; -
阻塞(暂停) Blocked
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; -
死亡 Dead
+ (void)exit;
3、线程的属性
线程有两个重要的属性:名称和优先级
3.1 名称 name
设置线程名用于记录线程,在出现异常时可以DeBug
3.2 优先级,也叫做“服务质量”。threadPriority,取值0到1.
优先级或者服务质量高的,可以优先调用,只是说会优先调用,但是不是百分之百的优先调用,这里存在一个概率问题,内核里的算法调度线程的时候,只是把优先级作为一个考虑因素,还有很多个因数会决定哪个线程优先调用。这点得注意注意!!!
下面是测试代码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//新建状态
NSThread *test1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
test1.name = @"test1";
//线程的优先级
test1.threadPriority = 1.0;
//就绪状态
[test1 start];
//新建状态
NSThread *test2= [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
test2.name = @"test2";
test2.threadPriority = 0;
//就绪状态
[test2 start];
}
//线程执行完成之后会自动销毁
- (void)demo {
for (int i = 0; i < 20; i++) {
NSLog(@"%d--%@",i,[NSThread currentThread]);
}
}
控制台
2016-02-16 22:43:28.182 05-线程状态[1241:78688] 0--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.182 05-线程状态[1241:78689] 0--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.182 05-线程状态[1241:78688] 1--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.182 05-线程状态[1241:78688] 2--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.182 05-线程状态[1241:78689] 1--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 3--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.183 05-线程状态[1241:78689] 2--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 4--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 5--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.183 05-线程状态[1241:78689] 3--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 6--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 7--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.183 05-线程状态[1241:78689] 4--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.183 05-线程状态[1241:78688] 8--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.184 05-线程状态[1241:78688] 9--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.184 05-线程状态[1241:78688] 10--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.184 05-线程状态[1241:78689] 5--<NSThread: x7fead050a250>{number = 3, name = test2}
2016-02-16 22:43:28.184 05-线程状态[1241:78688] 11--<NSThread: x7fead2017f30>{number = 2, name = test1}
2016-02-16 22:43:28.184 05-线程状态[1241: