iOS基础--多线程简单总结(NSThread、NSOperation、NSOperationQueue))

原来一切都来不及
多线程.png

多线程概念


  • 程序: 由源代码生成的可执行应用.
  • 进程: 一个正在运行的程序可以看做一个进程. (例如: 正在运行的QQ就是一个进程) , 进程拥有独立运行所需的全部资源.
  • 线程: 程序中独立运行的代码段.

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

  • 单线程:

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

  • 多线程:

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

  • 单线程,多线程区别 :

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

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

  • iOS多线程实现种类

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、线程间互斥应对的是这种场景: 多个线程操作统一资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 784评论 0 3
  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,226评论 21 42
  • 一、多线程简介: 所谓多线程是指一个 进程 -- process(可以理解为系统中正在运行的一个应用程序)中可以开...
    寻形觅影阅读 1,010评论 0 6
  • 没有去西安之前,西安在我的印象中就像《三枪拍案惊奇》里的大漠北的感觉,总是有一种这地方儿缺水的感觉先入为主,以至于...
    苗子爱吃肉阅读 437评论 2 1
  • 已是22点了,睡前照例不经意查阅下微信,竟发现我所在写作组的朋友们都在陆续地上传作业,十一月份虽说已没规定要交作业...
    与你画夕阳阅读 375评论 0 0