iOS技术难点技术iOS学习总结

iOS基础--多线程简单总结(NSThread、NSOperat

2016-04-10  本文已影响751人  云之君兮鹏
原来一切都来不及 多线程.png

多线程概念


注:一个进程是由一或多个线程组成. 进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行.

1、每个正在运行的程序(即 进程),至少包含一个线程, 这个线程叫 主线程
2、主线程在程序启动时被创建,用于执行mian函数
3、只有一个主线程的程序,称作单线程程序
3、在单线程程序中,主线程负责执行程序的所有代码(UI 展现以及刷新,网络请求, 本地存储等). 这些代码只能顺序执行, 无法并发执行 .

1、拥有多个线程的程序,称作多线程程序
2、iOS 允许用户自己开辟新的线程, 相对于主线程来讲, 这些线程, 称作子线程
3、可以根据需要开辟若干子线程
4、子线程和主线程 都是 独立 的运行单元, 各自的执行互不影响, 因此能够并发执行

1、单线程程序: 只有一个线程, 即主线程, 代码顺序执行,容易出现代码阻塞(页面假死)
2、多线程程序: 有多个线程, 线程间独立运行, 能有效的避免代码阻塞,并且提高程序的运行性能.

注意: iOS关于UI的添加和刷新必须在主线程中操作.//开发中依赖于多线程: 网络 :(主线程:(UI),子线程(取数据))

NSObject
NSThread
NSOperationQueue
GCD


NSObject 和 NSThread


#pragma mark ---------NSObject 开辟子线程
// NSObject 开辟子线程
// 参数 1: selector, 子线程执行的代码(方法名)
// 参数 2: 表示selector传递的参数
[self performSelectorInBackground:@selector(sayHi) withObject:@"hahah"];

#pragma mark ---------NSThread 手动开辟子线程
// NSThread 开辟一个子线程
// 参数 1: target
// 参数 2: action
// 参数 3: 传参
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sayHi) object:nil];
//开启子线程 [thread start];
//取消 (给线程发送结束消息,不会真正的取消掉线程,而是标记 这个被取消了)
[thread cancel];
//立即结束线程
[NSThread exit];
//对于NSObject 和 NSThread实现的多线程,在任务完成之后,线程会被自动释放
//判断一个线程是否正在执行
[thread isExecuting];
//判断一个线程是否完成了任务(是否执行完毕)
[thread isFinished];
#pragma mark -----------NSThread 自动开辟一个线程
//使用NSThread自动开辟一个线程
//不需要手动开启线程
[NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];

//几秒后执行某件事情
[self performSelector:@selector(time) withObject:self afterDelay:3.0f];
}
//让线程休眠2秒
[NSThread sleepForTimeInterval:2];

- (void)sayHi{
// [NSThread currentThread]; 获取当前的线程
NSLog(@" %@ ", [NSThread currentThread]);
// [NSThread mainThread]; 获取主线程
NSLog(@" %@ ", [NSThread mainThread]);
// [NSThread isMainThread] 判断当前线程是不是主线程
NSLog(@" %d ", [NSThread isMainThread]);

#pragma mark ----------NSObject
//NSObject中回到主线程去做某事
// 参数 1: 回到主线程做的事情
// 参数 2: 传递的参数
// 参数 3: NO:当前的线程任务已经结束才去做 YES: 执行完 selector任务后才执行当前线程的其他任务
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
\ #pragma mark -----------NSThread 手动
for (int i = 0; i < 10000; i++) { NSLog(@" %d ", i);
if (i == 5000) {
//关闭线程
// 写在哪里 哪个线程就关闭了, 注意 不要随意的使用. 使用的时候一定要注意当前的线程 是否主线程
[NSThread exit]; }
}
}

- (void)onMainThread{
    self.view.backgroundColor = [UIColor orangeColor];   
    NSLog(@"mian --> %@ ", [NSThread mainThread]);   
    NSLog(@"current --> %@", [NSThread currentThread]);
}```


-----
NSOperation和NSOperationQueue
------
------
- NSOperation :

>1、NSOperation类, 在MVC中属于M, 是用来封装单个任务相关的代码和数据的抽象类
2、因为它抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation) 来执行实际任务.
3、NSOperation(含子类),  只是一个操作, 本身无主线, 子线程之分, 可在任意线程中使用. 通常与NSOperationQueue结合使用.

- NSOperationQueue
>1、NSOperationQueue是操作队列,它用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务并行执行
2、其中NSOperation可以调节它在队列中的优先级 (使用addDependency: 设置依赖关系)
3、当最大并发数设置为1的时候,能实现线程同步 (串行执行);
-------

//NSOperation 是一个抽象类,不能直接使用
//NSOperation类及其子类本身不会进行线程的创建
\#pragma mark —————NSInvocationOperation
   //通过NSInvocationOperation类来创建一个NSOperation对象    
```code
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(hehehe) object:nil];`
    //operation 在单独使用的时候 一定要调用开始方法
   ` [operation start];```
\#pragma mark —————NSBlockOperation
     
```code
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{  
  NSLog(@"block main --> %@", [NSThread mainThread]);
  NSLog(@"block current --> %@", [NSThread currentThread]);    }];
//operation 在单独使用的时候 一定要调用开始方法
    [blockOperation start];```
 \#pragma mark —————NSOperationQueue
  `  NSOperationQueue *queue = [[NSOperationQueue alloc]init];`
     //队列添加operation子类,并调用方法
   ` [queue addOperation:operation];
    [queue addOperation:blockOperation]; `
  //依赖关系  (只有 参数线程 执行完,才能执行,之前是随机交错进行的)
  `  [operation addDependency:blockOperation];`
      //获取 系统提供的队列   
```code
 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];}
- (void)hehehe{       
NSLog(@"hehehe main --> %@", [NSThread mainThread]); 
NSLog(@"hehehe current --> %@", [NSThread currentThread]);    
NSLog(@" %d ", [NSThread isMainThread]);
}```
```code
- (void)touchesBegan:(NSSet<UITouch *> \*)touches withEvent:(UIEvent \*)event{       
//NSOperationQueue 是一个队列管理器,可以根据operation任务自己,分配线程,自己管理线程的生命周期  
//在开发过程中,我们不需要管理线程的创建和销毁 
//NSOperationQueue 创建的是n个并行的线程 
 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//最大线程并发数 
//设置这个参数之后,NSOperationQueue表示同时执行任务的最大数量 
//即使只执行一个任务,系统有时候也会开辟多个线程去执行这个任务 
 queue.maxConcurrentOperationCount = 1; 
for (int i = 0; i < 10; i++) ``
{  
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{   
         NSLog(@"current --> %@ main --> %@",[NSThread currentThread],[NSThread mainThread]);     }]; 
[queue addOperation:blockOperation];    }}```

------
GCD
-------
--------
##Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称处理系统.

###GCD提供函数实现多线程开发,性能更好,功能也更加强大.

- ###核心概念:
1、任务: 具有一个订功能的代码段.一般是一个block或者函数
2、分发队列: GCD以队列的方式进行工作,FIFO(先入先出队列)
3、GCD会根据分发队列的类型, 创建合适数量的线程执行队列中的任务

- ###GCD中的两种队列 dispatch_queue:
- SerialQueue(串行) : 一次只执行一个任务. Serial queue通常用于同步访问,特定的资源或数据.当你创建多个Serial queue时,虽然他们各自是同步执行的,但Serial queue于Serial queue之间是并发执行的.SerialQueue能实现线程同步.
- Concurrent(并发) : 可以并发地执行多个任务,但是遵守FIFO(先入先出队列 )

--------

--------

\#pragma mark ----------GCD串行队列
//     1) 系统提供的一个串行队列
//    使用系统提供的串行队列 (主队列,也就是在主线程里一次执行任务)
   ` dispatch_queue_t queue = dispatch_get_main_queue();`
 //     2) 创建一个串行队列
//     参数 1: 自己创建队列的名(苹果推荐使用反向域名去命名注意没有@)
//     参数 2: 系统提供好的一个宏(DISPATCH_QUEUE_SERIAL=NULL)
//     这种方式创建的队列,会开辟子线程去执行任务
 `   dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);`
------

------


\#pragma mark ----------GCD并行队列    //     1) 使用系统提供的并行队列
//    参数 1: 表示队列的优先级 (
         DISPATCH_QUEUE_PRIORITY_BACKGROUND,
         DISPATCH_QUEUE_PRIORITY_LOW,
         DISPATCH_QUEUE_PRIORITY_HEIGH,
         DISPATCH_QUEUE_PRIORITY_DEFAULT)
// 参数 2: 系统保留字段
`dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);`
//     2) 创建并行队列
   ` dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);`

--------

--------


\#pragma mark ----------GCD 功能
    //使用dispatch_async() 向队列添加任务,任务会排队执行
 // 任务虽会顺序排队执行,但如果用并发队列(CONCURRENT),可能输出顺序不一样.
       
```code
dispatch_async(queue, ^{ 
NSLog(@"1 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]);    });
dispatch_async(queue, ^{ 
NSLog(@"2 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]);    }); 
dispatch_async(queue, ^{ 
NSLog(@"3 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]);
    });    ```

 // dispatch_after()
//往队列中添加任务,任务不但会排队,还会在延迟的时间点执行    
```code
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
NSLog(@"已经3秒之后了");
    });```

 //dispatch_apply();
//往队列中添加任务,任务会重复执行n次
// 参数 1: 一共执行次数
// 参数 2: 执行的队列 
// 参数 3: 当前索引 

```code
dispatch_apply(3, queue, ^(size_t index) {        
NSLog(@" %zu ", index);
    });```

//分组   
//创建一个分组  
`dispatch_group_t group = dispatch_group_create(); `
//创建一个队列
`dispatch_queue_t queue = dispatch_queue_create("000", DISPATCH_QUEUE_CONCURRENT); `      
//向分组中添加一个任务
` dispatch_group_async(group, queue, ^{ 
NSLog(@"1");    }); `
//向分组添加 最后执行的任务(不能添加为第一个) 
`dispatch_group_notify(group, queue, ^{  
NSLog(@"last one");
    })`
 //将任务添加到队列,此任务执行的时候,其他任务停止执行,所以它输出顺序不改变
    
```code
dispatch_barrier_async(queue, ^{ 
NSLog(@"不变位置的2");    }); 
dispatch_group_async(group, queue, ^{  
 NSLog(@"3");
    });
}```

####**dispatch_once() //将任务添加到队列, 但任务在程序运行过程中,只执行一次**

//创建单例类
\#import "MyObject.h"static MyObject *object = nil;

\+ (MyObject *)sharedMyObject{ 
//表示同意时间内, 只有一个线程可以访问block块里面的内容  
//dispatch_once 系统封装好的代码块 
```code
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ 
 if (object == nil) {
object = [MyObject new];    }    });    
return object;}```

>dispatch_sync() //将任务添加到队列, block不执行完,下面代码不会执行
dispatch_async() //将任务添加到队列,不等内部block执行完,就去执行下面代码
dispatch_async_f() //将任务添加到队列, 任务是函数,非Block

--------
线程间的通信
---------
---------
>线程间通信分为两种:
- 主线程进入子线程 (前面的方法都可以)
- 子线程回到主线程

\#pragma mark ----------NSObject
 \ -(void)viewDidLoad{
//NSObject中回到主线程去做某 
 // 参数 1: 回到主线程做的事情    
// 参数 2: 传递的参数    
// 参数 3: 知道当前的线程已经结束才去做
 ```code
   [self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
}`
\- (void)onMainThread{
    `self.view.backgroundColor = [UIColor orangeColor];   
    NSLog(@"mian --> %@ ", [NSThread mainThread]);   
    NSLog(@"current --> %@", [NSThread currentThread]);`
}
\#pragma mark —————GCD

\- (void)loadData{ 
`NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
 if (error == nil) { 
dispatch_async(dispatch_get_main_queue(), ^{ 
//这里去做更新UI的事情
 });   }    }];
}```

--------
- ###线程互斥

> 1、多线程并行编程中,线程间同步与互斥是一个很有技巧也是很容易出错的地方.
2、线程间互斥应对的是这种场景: 多个线程操作统一资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确.
上一篇下一篇

猜你喜欢

热点阅读