在学习多线程之前首先搞清楚以下几个问题。
并发:在同一时刻,只有一条指令被执行,多条指令进行快速切换执行。
并行:在同一时刻,多个处理器可以处理多条指令
1.什么是进程?
一个运行的程序就是一个进程或一个任务
多个进程之间是具有相互独立的内存空间。
2.什么是线程?
进程当中代码的一个执行路径。
多线程:也就是说代码有多个执行路径。
3.进程与线程的关系?
一个进程至少包含一个线程(主线程),线程是程序的执行流
主线程是其他线程的父线程,所有的界面的显示操作必须在主线程进行。
 一个进程中包含多个线程。多个线程共享进程当中的内存空间。
4.单核与多核处理器下多线程的不同?
单核处理器:CPU是按时间片进行轮转调度执行不同的线程,在切换线程时需要保存和恢复线程的上下文(CPU中寄存器的内容)。
5.多线程中线程同步的问题?
多个线程对同一资源进行竞争的情况下会出现线程同步的问题。
6.多线程的优势与难点?
• 优势
  –  充分发挥多核处理器优势,将不同线程任务分配给不同的处理器,真正进入“并行运算”状态
–  将耗时、轮询或者并发需求高等任务分配到其他线程执行, 并由主线程负责统一更新界面会使得应用程序更加流畅,用 户体验更好 
–  当硬件处理器的数量增加,程序会运行更快,而无需做任何 调整 
• 难点 
  –  共享资源的“争夺” 
–  多线程是为了同步完成多项任务,不是为了提高运行效率, 而是为了通过提高资源使用效率来提高系统的整体性能
7.多线程使用注意事项 
• 线程使用不是无节制的– iOS中的主线程的堆栈大小是1M
  – 从第二个线程开始都是512KB
  – 这些数值不能通过编译器开关或线程API函数更改
• 只有主线程有直接修改UI的能力

iOS的三种多线程技术

1. NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程)
  以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去 关心线程的具体使用问题

2  NSOperation/NSOperationQueue 面向对象的线程技术 
3  GCD —— Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术
以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高 的使用越简单,也是Apple最推荐使用的。但是就目前而言,iOS的开发者, 需要了解三种多线程技术的基本使用过程。因为很多框架技术分别使用 了不同多线程技术。

三种多线程技术的对比 
  • NSThread:
  –  优点:NSThread 比其他两个轻量级,使用简单 
  –  缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以 及唤醒等。线程同步对数据的加锁会有一定的系统开销 
  • NSOperation: 
  –  不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上 
  –  NSOperation是面向对象的 • GCD: 
–  Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。 iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术
一、NSThread实现多线程
首先先通过一个模拟售票的案例来让大家看看多线程的使用。创建了两个线程分别是售票线程-1和售票线程-2分别模拟两个售票员。
#import "ViewController.h"
@interface ViewController ()
{NSUInteger _tickets;//总票数
}
@property (weak, nonatomic) IBOutlet UITextView *textView;
@end
@implementation ViewController
- (void)viewDidLoad {[super viewDidLoad];self.textView.text = @"";self.textView.layoutManager.allowsNonContiguousLayout = NO;//初始化总票数_tickets = 20;//创建线程,并启动线程//1.售票线程1NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSellMethod) object:nil];//设置线程的名字[thread1 setName:@"售票线程-1"];//启动线程
    [thread1 start];//2.售票线程2NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSellMethod) object:nil];//设置线程的名字[thread2 setName:@"售票线程-2"];//启动线程
    [thread2 start];
}
/***  更新UI*/
-(void)appendTextView:(NSString *)text
{//1.获取textView中已有的内容NSMutableString *string = [[NSMutableString alloc]initWithString:self.textView.text];NSRange range = NSMakeRange(string.length, 1);//2.追加新的内容并显示[string appendString:[NSString stringWithFormat:@"%@\n",text]];[self.textView setText:string];//3.滚动视图
    [self.textView scrollRangeToVisible:range];
}
/***  线程执行的方法-售票*/
-(void)threadSellMethod
{while (YES){//判断是否有剩余票数if (_tickets >  0){//1.更新UINSString *info = [NSString stringWithFormat:@"总票数:%ld,当前线程:%@",_tickets,[[NSThread currentThread]name]];[self performSelectorOnMainThread:@selector(appendTextView:) withObject:info waitUntilDone:YES];//2.售票_tickets--;//3.模拟休息时间if ([[[NSThread currentThread]name] isEqualToString:@"售票进程-1"]){[NSThread sleepForTimeInterval:0.3f];}else{[NSThread sleepForTimeInterval:0.2f];}}else{//1.更新UINSString *info = [NSString stringWithFormat:@"已无剩余票数,当前线程:%@",[[NSThread currentThread]name] ];[self performSelectorOnMainThread:@selector(appendTextView:) withObject:info waitUntilDone:YES];//2.退出线程break;}}
}
@end

运行结果图:
细心地朋友可能已经发现了,有些结果在上面出现了两次,这是关于竞争资源的同步问题,在这不多做解释,稍后会另作详解,现在先来对代码进行分析,掌握NSThread是如何实现多线程。
NSThread创建线程的两种方式:
1.类方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; 
2.实例方法
- (id)initWithTarget:(id)target selector: (SEL)selector object:(id)argument; 
• 参数说明:
– selector:线程执行的方法,只能有一个参数,不能有返回值 
– target:selector消息发送的对象
– argument:传输给target的唯一参数,也可以是nil

创建线程的实例方法与类方法的区别在于:实例方法返回的是一个NSThread对象,必须调出start方法启动线程;而类方法不会返回NSThread对象,因为这种方法会直接创建并启动线程,我们在案例中使用的是NSThread的实例方法进行创建多线程。
//NSThread的实例方法创建多线程//1.售票线程1NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSellMethod) object:nil];//设置线程的名字[thread1 setName:@"售票线程-1"];//启动线程
    [thread1 start];//等效于://NSThread的类方法创建多线程[NSThread detachNewThreadSelector:@selector(threadSellMethod) toTarget:self withObject:@"售票线程-1"];

NSThread中还有一些类方法。
+currentThread:是NSThread类的类方法,该方法总是返回当前正在执行的线程对象。
程序可以通过setName:方法为线程设置名字,也可以通过name方法返回指定线程的名字。
线程的状态
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。即使线程开始运行以后,它也不可能一直“霸占”着CPU独自运行,所以CPU需要在多个线程之间切换,于是线程状态也会多次在运行、就绪状态之前切换。
当程序创建了一个线程之后,该线程就处于新建状态,此时它和其他OC对象一样,仅仅由系统为其分配了一个内存,并初始化了成员变量的值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程的线程质性体。
启动线程使用start方法,线程启动之后并不是立即进入运行状态,线程被启动后处于就绪状态,当系统调度线程后,线程才会进入运行状态。
终止子线程
线程会以以下3种方式之一结束,结束后就处于死亡状态
1.线程执行体方法执行完成,线程正常结束。
2.线程执行过程中出现了错误。
3.直接调用NSThread类的exit方法来中止当前正在执行的线程。
当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有了和主线程相同的地位,它不会受主线程的影响。
为了测试某个线程是否正在运行,可以调用线程对象的isExecuting、isFinished,当线程正处于执行过程中时,调用isExecuting方法就会返回YES;当线程执行完成后,调用isFinished方法就会返回YES;
如果希望咋UI线程中终止子线程,NSThread并没有提供方法来终止某个子线程,虽然NSThread提供了cancel方法,但该方法仅仅是改变了该线程的状态,导致该线程的isCancelled方法返回NO,而不是真正终止该线程。
线程睡眠
如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用NSThread类的静态方法。sleepXXX方法来完成。NSThread类提供了如下两个控制线程暂停的类方法。
+(void)sleepUntilDate:(NSDate *)aDate:让当前正在执行的线程暂停到aDate代表的时间,并进入阻塞状态。
+(void)sleepForTimeInterval:(NSTimeInterval)ti:让当前正在执行的线程暂停ti秒,并进入阻塞状态。
如:[NSThread  sleepForTimeInterval:0.5f];
多线程的同步安全问题
为了解决多线程的同步安全问题,在OC中给出了几种方式,
1.同步锁(NSLock)
NSLock是控制多个线程对共享资源进行访问的工具,通常锁提供了对共享资源的独占访问,每次只能有一个线程对NSLock对象加锁,线程开始访问共享资源之前应先获得NSLock对象。
2.使用@synchronized实现同步
为了解决竞争资源不同步的问题,OC的多线程支持引入了同步,使用同步的通用方法就是    @synchronized修饰代码块,被@synchronized修饰的代码块可简称为同步代码块。同步代码块格式如下:
@synchronized(obj)
{
//此处的代码就是同步代码块
}
任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对同步监视器的锁定。
任何线程在修改指定资源之前,首先都要对该资源加锁,在加锁期间其他线程无法修改该资源,当该线程修改完成后,释放对该资源的锁定。通过这种方式就可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(也可称为临界区),所以同一时刻最多只有一个线程处于临界区,从而保证了线程的安全性。
现在对上个案例进行修改,来解决多线程的同步问题。
#import "ViewController.h"
@interface ViewController ()
{NSUInteger _tickets;//总票数
}
@property (weak, nonatomic) IBOutlet UITextView *textView;
//@property(strong,nonatomic)NSLock *lock;
//@property(strong,nonatomic)NSCondition *condition;

@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.textView.text = @"";self.textView.layoutManager.allowsNonContiguousLayout = NO;//初始化总票数_tickets = 20;//创建线程锁
//    self.lock = [[NSLock alloc]init];
//    self.condition = [[NSCondition alloc]init];//创建线程,并启动线程//1.售票线程1NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSellMethod) object:nil];//设置线程的名字[thread1 setName:@"售票线程-1"];//启动线程
    [thread1 start];//2.售票线程2NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSellMethod) object:nil];//设置线程的名字[thread2 setName:@"售票线程-2"];//启动线程
    [thread2 start];
}/***  更新UI*/
-(void)appendTextView:(NSString *)text
{//1.获取textView中已有的内容NSMutableString *string = [[NSMutableString alloc]initWithString:self.textView.text];NSRange range = NSMakeRange(string.length, 1);//2.追加新的内容并显示[string appendString:[NSString stringWithFormat:@"%@\n",text]];[self.textView setText:string];//3.滚动视图
    [self.textView scrollRangeToVisible:range];
}
/***  线程执行的方法-售票*/
-(void)threadSellMethod
{while (YES){//判断是否有剩余票数if (_tickets >  0){/***  加锁应该是在有竞争资源的地方*///访问竞争资源前进行加锁
//            [self.lock lock];
//            [self.condition lock];/***  需要同步的代码块放入synchronized块中*/@synchronized(self){@autoreleasepool{//1.更新UINSString *info = [NSString stringWithFormat:@"总票数:%ld,当前线程:%@",_tickets,[[NSThread currentThread]name]];[self performSelectorOnMainThread:@selector(appendTextView:) withObject:info waitUntilDone:YES];//2.售票_tickets--;}}//访问完竞争资源后进行解锁
//            [self.condition unlock];
//            [self.lock unlock];//3.模拟休息时间if ([[[NSThread currentThread]name] isEqualToString:@"售票进程-1"]){[NSThread sleepForTimeInterval:0.3f];}else{[NSThread sleepForTimeInterval:0.2f];}}else{//1.更新UINSString *info = [NSString stringWithFormat:@"已无剩余票数,当前线程:%@",[[NSThread currentThread]name] ];[self performSelectorOnMainThread:@selector(appendTextView:) withObject:info waitUntilDone:YES];//2.退出线程break;}}
}
@end

运行结果如图:

现在再来看运行结果,发现上个案例中出现的问题已经解决了,每个只会出现一次。

  再来看程序的代码部分,案例中使用了给出了三个解决同步安全问题的办法,代码中标出的蓝色部分是NSLock、NSCondition的用法,案例中使用的是@synchronized同步块。解决同步安全的逻辑思路“加锁”->"修改"->"解锁"。当当前线程正在使用竞争资源时,阻挡掉其他线程对竞争资源的访问。

  在程序中还用到了内存管理的自动释放池。

内存管理对于多线程非常重要
  • Objective-C可以凭借@autoreleasepool使用内存资源,并需要时回收资源
• 每个线程都需要有@autoreleasepool,否则可能会出现内存泄漏
NSOperation & NSOperationQueue实现多线程
•NSOperation的两个子类
1.NSInvocationOperation
2.NSBlockOperation
3.子类化NSOperation,然后重写main方法
•工作原理:
1.用NSOperation封装要执行的操作
2.将创建好的NSOperation对象放NSOperationQueue中
3.启动OperationQueue开始新的线程执行队列中的操作
•注意事项:
1.使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
2.使用以下方法可以控制并发的线程数量:

  - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

在此案例中,不多做解释,直接代码搞起。

第一个案例通过NSInvocationOperation类进行实现

#import "ViewController.h"@interface ViewController ()
{NSUInteger _tickets;
}
@property (weak, nonatomic) IBOutlet UITextView *textView;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//初始化票数_tickets = 30;//设置textViewself.textView.text = @"";//创建NSOperationNSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票线程-1"];NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票线程-2"];//创建NSOperationQueue队列NSOperationQueue *queue = [[NSOperationQueue alloc]init];   //为operation1添加依赖,只有当operation2执行完毕时,operation1才能执行。
    [operation1 addDependency:operation2];//将operation加入到队列
    [queue addOperation:operation1];[queue addOperation:operation2];
}#pragma mark - 更新UI
-(void)appendTextView:(NSString *)text
{//1.先获取原先的内容NSMutableString *str = [[NSMutableString alloc]initWithString:self.textView.text];NSRange range = NSMakeRange(str.length, 1);//2.追加新的内容[str appendString:[NSString stringWithFormat:@"%@\n",text]];[self.textView setText:str];//3.滚动textView
    [self.textView scrollRangeToVisible:range];
}
#pragma mark - 线程调用的方法
-(void)operationSaleMethod:(NSString *)name
{while (YES){//判断剩余票数if (_tickets > 0){@synchronized(self){NSString *info = [NSString stringWithFormat:@"总的票数:%ld,当前线程:%@",_tickets,name];[[NSOperationQueue mainQueue]addOperationWithBlock:^{[self appendTextView:info];}];//卖票_tickets--;}if ([name isEqualToString:@"售票线程-1"]){//线程休眠[NSThread sleepForTimeInterval:0.3f];}else{[NSThread sleepForTimeInterval:0.2f];}}else{//更新UINSString *info = [NSString stringWithFormat:@"已无剩余票数,当前线程:%@",name];[[NSOperationQueue mainQueue]addOperationWithBlock:^{[self appendTextView:info];}];       break;}}
}
@end

案例2:通过NSBlockOperation进行实现

#import "ViewController.h"@interface ViewController ()
{NSInteger _tickets;
}
@property (weak, nonatomic) IBOutlet UITextView *textView;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//初始化票数_tickets = 30;//设置textViewself.textView.text = @"";//创建NSOperationQueue队列NSOperationQueue *queue = [[NSOperationQueue alloc]init];//设置线程的并行数量[queue setMaxConcurrentOperationCount:2];//在队列中添加block的operation[queue addOperationWithBlock:^{[self operationSaleMethod:@"售票线程-1"];}];[queue addOperationWithBlock:^{[self operationSaleMethod:@"售票线程-2"];}];NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{[self operationSaleMethod:@"售票线程-3"];}];//将线程添加到队列中去
    [queue addOperation:operation];}#pragma mark - 更新UI
-(void)appendTextView:(NSString *)text
{//1.先获取原先的内容NSMutableString *str = [[NSMutableString alloc]initWithString:self.textView.text];NSRange range = NSMakeRange(str.length, 1);//2.追加新的内容[str appendString:[NSString stringWithFormat:@"%@\n",text]];[self.textView setText:str];//3.滚动textView
    [self.textView scrollRangeToVisible:range];
}
#pragma mark - 线程调用的方法
-(void)operationSaleMethod:(NSString *)name
{while (YES){//判断剩余票数if (_tickets > 0){@synchronized(self){NSString *info = [NSString stringWithFormat:@"总的票数:%ld,当前线程:%@",_tickets,name];[[NSOperationQueue mainQueue]addOperationWithBlock:^{[self appendTextView:info];}];//卖票_tickets--;}if ([name isEqualToString:@"售票线程-1"]){//线程休眠[NSThread sleepForTimeInterval:0.3f];}else{[NSThread sleepForTimeInterval:0.2f];}}else{//更新UINSString *info = [NSString stringWithFormat:@"已无剩余票数,当前线程:%@",name];[[NSOperationQueue mainQueue]addOperationWithBlock:^{[self appendTextView:info];}];break;}}
}
@end

以上两个案例的运行结果一致,如图:

如果希望当其中一个线程执行完毕之后,再去执行另外一个线程,可以通过依赖来实现。

//为operation1添加依赖,只有当operation2执行完毕时,operation1才能执行。[operation1 addDependency:operation2];

累死啦!!

转载于:https://www.cnblogs.com/xjf125/p/4857658.html

iOS_多线程(一)相关推荐

  1. IOS_多线程_ASI_AFN_UIWebView

    H:/0730/00_多线程4种售票_ViewController.h // // ViewController.h // 卖票 // // Created by apple on 13-7-29. ...

  2. Java 多线程概述

    多线程技术概述 1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间. 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换.并发执行,一个进程最少有一个线程, ...

  3. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  4. RPC 笔记(08)— socket 通信(多进程多线程服务器)

    在上一节中如果并行的客户端连接数超过了默认开启进程的数量,那么后来的客户端请求将会阻塞,为了不阻塞新的客户端,我们可以将进程的单线程改成多线程即可. ​ 服务端代码: import json impo ...

  5. Python 多线程总结(2)— 线程锁、线程池、线程数量、互斥锁、死锁、线程同步

    主要介绍使用 threading 模块创建线程的 3 种方式,分别为: 创建 Thread 实例函数 创建 Thread 实例可调用的类对象 使用 Thread 派生子类的方式 多线程是提高效率的一种 ...

  6. Python 多线程总结(1)- thread 模块

    thread 模块 1. 单线程 首先看下单线程程序运行的例子,如下所示, import timedef loop0():print 'start loop0 begin', time.ctime() ...

  7. Python多线程调试

    有时候程序是多线程的,调试的时候可能跑到别的线程了. 这个时候把thread.start变成threa.run就好了,就会执行完当前线程再执行下一个. for thread in threads:th ...

  8. python究竟要不要使用多线程

    阅读目录 1. 先来看两个例子 2. python虚拟机机制如何控制代码执行? 3. python多线程究竟有没有用? 4. python多进程执行原理 在总结concurrent.futures库之 ...

  9. Python多线程(3)——Queue模块

    Python多线程(3)--Queue模块 Queue模块支持先进先出(FIFO)队列,支持多线程的访问,包括一个主要的类型(Queue)和两个异常类(exception classes). Pyth ...

最新文章

  1. mxOutlookBarPro中,button是通过for语句动态生成的,如何取得当前单击button的标题呢?...
  2. PL/SQL Developer下设置“长SQL自己主动换行”
  3. How to: Display a Gradient Fill
  4. 【渝粤教育】国家开放大学2018年春季 0273-21T中国现代文学 参考试题
  5. sublime php快捷键,分享Sublime Text 3快捷键精华版!
  6. Python自动化测试|如何解决前置模块及数据依赖(二)
  7. C++笔记-Qt中使用Lambda时[]中的形式
  8. springboot 控制台输出错误信息_SpringBoot 三招组合拳,手把手教你打出优雅的后端接口...
  9. JBox - 模态窗口,工具提示和消息 jQuery 插件
  10. Unity 2018.3.8 f1 个人版的Standard Assets在哪里下载?
  11. Fast-RTPS初体验
  12. 设计模式(二)Adapter模式
  13. 电气领域相关数据集(目标检测,分类图像数据及负荷预测),输电线路图像数据
  14. 什么才是真正的架构设计
  15. oracle数据库按日期查询,关于Oracle数据库日期范围查询的两种实现方法详解,oracle详解...
  16. ios链接xcode跑自动化意外中断?可以用pyautogui试试
  17. 单点登录 Ucenter 分析
  18. Mac 双系统bootcamp不能正常卸载windows系统
  19. 计算机作业我家乡的变化英语作文,我家乡的变化英语作文(精选12篇)
  20. 自己更换云平台绑定QQ号的方法

热门文章

  1. mootools1.3.1源码解读
  2. 在同一台电脑上同时使用IE6和IE7
  3. Flask之扩展flask-migrate
  4. 基于场景建模的自动化配置
  5. 76 道 Oracle Goldengate 面试问题
  6. 深度学习与计算机视觉系列(8)_神经网络训练与注意点
  7. 英特尔变身数据公司 未来最大数据市场定是中国
  8. sql server 数据库性能忧化
  9. mac环境下安装xampp
  10. CSS布局之品字布局