概念:
单线程:程序是按从上到下顺序执行的,会有时间延迟
多线程:可以同时进行
NSLog(@" "); //会很耗时的,调试完应该关掉,也可用断点+po来输出对象```
#####1. 单线程:
修改界面操作不是像代码一样马上就改,为了节省效率,而是首先是记录一下,代码执行完之后统一渲染出来,只要有耗时的代码就会使得界面加载延迟(界面假死(阻塞),用户体验灰常不好)
- **解决:**
>1)、**不能在主线程里面做耗时操作**,放在子线程中,主线程用来接受用户交互,界面渲染等操作,让它保持畅通。
>2)、**不能把修改页面的代码放到子线程**(因为如果修改页面的代码放到子线程,修改之后还有别的事情要做的话,界面也会等别的事情处理完才展示,会出现界面响应不及时的情况)
- **耗时操作:**
> 1、所有和网络相关的
2、加载大文件
3、大量运算
4、睡眠代码[NSThread sleepForTimeInterval:2];
----
#####2. 开启线程有三种:
>1)、**NSThread**:
2)、**GCD:** 中央任务分发,由c语言实现,支持更底层(block里面是子线程),有线程队列,有串行与并行
3)、**NSOperation:** OC的,有线程队列,没有并,串行的概念,也可以实现并行,可以实现线程之间的关系(依赖关系,可以控制先后执行,其他不可以)
######1)、NSThread
NSLog(@"%@"[NSThread currentThread]); //输出线程名
- 开启子线程
//1、开启一个子线程去立即执行addViews方法,代码执行完线程自动开始执行
[NSThread detachNewThreadSelector:@selector(addViews) toTarget:self withObject:nil]; //nil什么也没有传过去
//2、NSThread 第二种写法:
NSThread *t1 = [[NSThread alloc] initWIthTarget:self selector"@selecteor(newAction:) object:nil];
t1.name = @"2号线程"; //可以給线程加名字
//开始执行
[t1 start];
```
- 回到主线程调用修改页面的方法
- (void)addViews{
for (int i=0; i<5; i++) {
// for (int i=0; i<10000; i++) {
// NSLog(@"%d",i);
// }
[NSThread sleepForTimeInterval:1]; //耗时操作 睡眠1秒
//回到主线程调用修改页面的方法 waitUntilDone是否等待主线程完成之后子线程再继续执行
//一般是 NO, Yes2边(主,子线程)执行结果有关联的时候用
[self performSelectorOnMainThread:@selector(updateUI:) withObject:@(i) waitUntilDone:NO]; //传个执行次数的参数过去
}
}
// 主线程调用方法
- (void)updateUI:(NSNumber *)number{
int i = [number intValue];
//修改页面的代码
UIView *v = [[UIView alloc]initWithFrame:CGRectMake(100, i*110, 100, 100)];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];
}```
----
######2)、 GCD:不需要添加多余的方法
//创建 串行队列
dispatch_queue_t myQueue = dispatch_queue_create("myQ", NULL);
//在队列中开启线程并执行
dispatch_async(myQueue, ^{
for (int i=0; i<5; i++) {
NSLog(@"线程1-%d",i);
}
//在队列中开启线程并执行
});
dispatch_async(myQueue, ^{
for (int i=0; i<5; i++) {
NSLog(@"线程2-%d",i);
}
});
// 得到 并行队列
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myQueue, ^{
for (int i=0; i<5; i++) {
NSLog(@"线程1-%d",i);
}
//在队列中开启线程并执行
});
dispatch_async(myQueue, ^{
for (int i=0; i<5; i++) {
NSLog(@"线程2-%d",i);
}
});
```
3、NSOperation: 不需要添加多余的方法,用block
//默认并行:
//创建线程队列
NSOperationQueue *myQueue = [NSOperationQueue new];
//创建线程1
NSOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(newThreadAction) object:nil];
//创建线程2
NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//不需要添加方法
for (int i=0; i<3; i++) {
NSLog(@"线程二 %d",i);
}
}];
//把线程添加到队列中 同时线程会自动开始执行
[myQueue addOperation:op1];
[myQueue addOperation:op2];
//线程1 的调用方法
- (void)newThreadAction {
for (int i=0; i<3; i++) {
NSLog(@"线程一 %d",i);
}
}
//实现串行:
//添加约束
//1、设置线程并行的数量(1,为一个一个执行实现串行效果)
[myQueue setMaxConcurrentOperationCount:1];
//2、设置线程之间的关系 让op2 依赖于 op1,先执行op1
[op1 addDependency:op2];
3. 多线程安全
当多条线程去同时修改一份数据的时候,有可能会出现安全问题(买票等等)
解决:通过同步代码块
方法1: @synchronized(self) {}
- (void)viewDidLoad {
[super viewDidLoad];
self.totalCount = 100;
NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(sellAction) object:nil];
t1.name = @"1号窗口";
[t1 start];
NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(sellAction) object:nil];
t2.name = @"2号窗口";
[t2 start];
}
- (void)sellAction{
while (YES) {
//通过线程同步 让某段核心代码 只能有一个线程执行(加锁)
@synchronized(self) {
NSString *name = [NSThread currentThread].name;
NSLog(@"%@开始卖%d号票",name,self.selledCount+1);
[NSThread sleepForTimeInterval:.5];
self.selledCount++;
NSLog(@"%@卖掉了%d号票 还剩%d张",name,self.selledCount,self.totalCount-self.selledCount);
}
}
}```
######方法2:NSLock
//声明属性
@property(nonatomic, strong)NSLock *myLock;
- (void)sellAction{
while (YES) {
//通过线程同步 让某段核心代码 只能有一个线程执行(加锁)
// @synchronized(self) {
[self.myLock lock];
NSString *name = [NSThread currentThread].name;
NSLog(@"%@开始卖%d号票",name,self.selledCount+1);
[NSThread sleepForTimeInterval:.5];
self.selledCount++;
NSLog(@"%@卖掉了%d号票 还剩%d张",name,self.selledCount,self.totalCount-self.selledCount);
// }
[self.myLock unlock];
}
}```
方法3:NSCondition
//声明属性
@property(nonatomic,strong)NSCondition *myCondition;
- (void)sellAction{
while (YES) {
//通过线程同步 让某段核心代码 只能有一个线程执行(加锁)
// @synchronized(self) {
[self.myCondition lock];
NSString *name = [NSThread currentThread].name;
NSLog(@"%@开始卖%d号票",name,self.selledCount+1);
[NSThread sleepForTimeInterval:.5];
self.selledCount++;
NSLog(@"%@卖掉了%d号票 还剩%d张",name,self.selledCount,self.totalCount-self.selledCount);
// }
[self.myCondition unlock];
}
}```