IOS中的多线程

来源:互联网 发布:哥伦比亚冲锋衣 知乎 编辑:程序博客网 时间:2024/06/01 08:26

一、操作系统中的几个基本概念

进程(process):指一个正在运行的可执行程序(应用程序),它可以包含多个线程。
线程(thread):指独立执行的代码片段,最小的执行单位。一个进程中所有的任务都在线程中执行。
任务(task):表示需要执行的工作。它是一个抽象的概念。

线程的三种状态:运行(running)、就绪(ready)、阻塞(blocked)。线程持续在这三个状态之间切换,直到它最终退出或者进入中断状态。当你显式的中断线程的时候,线程永久停止,且被系统回收。

单线程:在单个线程中任务的执行是串行的。如果在1个线程中执行多个任务,那么只能一个接一个的按顺序执行这些任务。所以在同一个线程中,是不存在资源不同步的。

多线程:一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。所以多线程可以提高程序的执行效率。

多线程的原理:同一时刻,CPU只能处理1条线程,所以多线程并发执行其实就是cpu快速的在不同的线程之间进行调度(有多种调度算法),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。所以如果在一个应用程序中开辟了非常多的线程,就会让CPU来回在多条线程之间进行切换,会消耗大量的CPU资源,从而导致每条线程被调度执行的频率降低,结果就是线程的执行效率降低。

多线程的优缺点
优点:提高程序执行效率;提高资源利用利率(cpu、内存利用率)。
缺点:创建线程是有开销的,ios下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但是必须是4K的倍数,并且小于16K),创建线程的时间大约是90毫秒;如果开启大量线程,会降低程序的性能;线程越多,CPU在调度线程上的开销就越大;程序设计更复杂,比如多线程间的通信,资源同步等。

ios中多线程的实现方案:
这里写图片描述

二、线程同步

概念

线程同步:多条线程按顺序的执行任务,即多条线程在同一条线上执行。

多线程的安全隐患及解决方式

隐患:
一块资源可能被多个线程共享,比如多个线程同时访问并操作一个对象、同一个变量、同一个文件等。当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

解决方法:
为操作资源的代码片段添加互斥锁。也就是在读取和改变资源的代码片段。切记,在同一个线程不会出现资源不同步的现象,所谓的资源不同步一定是在多线程中才会发生的。
互斥锁的使用格式:

@synchronized(锁对象){ //操作资源的代码片段 }// 锁定1份代码只用1把锁,用多把锁是无效的

互斥锁的优缺点:
优点:能有效防止因多线程抢夺资源造成的数据安全问题。
缺点:需要消耗大量的CPU资源

互斥锁使用前提:多条线程争夺同一块资源

OC中的原子性和非原子性:
atomic:原子属性,为setter方法加锁(默认就是atomic)。
nonatomic:非原子属性,不会为setter方法加锁。

ios开发的建议:
- 所有的属性都声明为nonatomic
- 尽量避免多线程抢夺同一块资源
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减少客户端的压力。

eg:模拟窗口卖票
1.不加锁情况下:

////  ViewController.m//  线程安全////  Created by zhangqi on 15/3/2016.//  Copyright (c) 2016 zhangqi. All rights reserved.//#import "ViewController.h"@interface ViewController ()@property (nonatomic,strong) NSThread *thread01;@property (nonatomic,strong) NSThread *thread02;@property (nonatomic,strong) NSThread *thread03;@property (nonatomic,assign) NSInteger ticketCount;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    self.ticketCount = 100;    self.thread01 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];    self.thread01.name = @"01窗口";    self.thread02 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];    self.thread02.name = @"02窗口";    self.thread03 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];    self.thread03.name = @"03窗口";}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    [self.thread01 start];    [self.thread02 start];    [self.thread03 start];}- (void)saleTicket{    while (1){            if (self.ticketCount > 0) {                self.ticketCount--;                NSLog(@"%@买票1张,余票:%zd张",[NSThread currentThread].name,self.ticketCount);            }else{                NSLog(@"票卖光了...");                break;            }    }}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}@end

运行结果:

2016-03-15 11:41:48.640 线程安全[62060:10112864] 01窗口买票1张,余票:97张2016-03-15 11:41:48.639 线程安全[62060:10112865] 02窗口买票1张,余票:98张2016-03-15 11:41:48.639 线程安全[62060:10112866] 03窗口买票1张,余票:98张2016-03-15 11:41:48.640 线程安全[62060:10112864] 01窗口买票1张,余票:96张2016-03-15 11:41:48.640 线程安全[62060:10112865] 02窗口买票1张,余票:95张2016-03-15 11:41:48.641 线程安全[62060:10112866] 03窗口买票1张,余票:94张2016-03-15 11:41:48.641 线程安全[62060:10112864] 01窗口买票1张,余票:93张……2016-03-15 11:41:48.657 线程安全[62060:10112865] 票卖光了...2016-03-15 11:41:48.657 线程安全[62060:10112866] 票卖光了...2016-03-15 11:41:48.657 线程安全[62060:10112864] 票卖光了...

2.加锁情况

- (void)saleTicket{    while (1){        @synchronized(self){            if (self.ticketCount > 0) {                self.ticketCount--;                NSLog(@"%@买票1张,余票:%zd张",[NSThread currentThread].name,self.ticketCount);            }else{                NSLog(@"票卖光了...");                break;            }        }    }}

运行结果:

2016-03-15 11:46:14.674 线程安全[62161:10117397] 02窗口买票1张,余票:992016-03-15 11:46:14.674 线程安全[62161:10117398] 03窗口买票1张,余票:982016-03-15 11:46:14.675 线程安全[62161:10117396] 01窗口买票1张,余票:972016-03-15 11:46:14.675 线程安全[62161:10117397] 02窗口买票1张,余票:96张……2016-03-15 11:46:14.701 线程安全[62161:10117398] 票卖光了...2016-03-15 11:46:14.702 线程安全[62161:10117397] 票卖光了...2016-03-15 11:46:14.702 线程安全[62161:10117396] 票卖光了...

三、线程间的通信

四、NSOperation

简介

NSOperation的作用:
NSOperation配合NSOperationQueue也能实现多线程编程

NSOperation和NSOperationQueue实现多线程的具体步骤:
- 首先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出来的NSOperation封装的操作放到一条新线程中执行

NSOperation的子类:
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类。使用其子类的方式有三种:
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法

NSInvocationOperation的使用:
使用创建出的NSInvocationOperation对象调用start()方法后,默认情况下,调用了start方法后并不会开启一条新线程去执行操作,而是在当前线程同步执行操作。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。
eg:

- (void)testInvocationOperation{    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel) object:nil];    [invocationOperation start];}- (void)invocationSel{    NSLog(@"当前线程是---%@",[NSThread currentThread]);}

运行结果:
2016-03-18 13:44:44.832 ScanboxOperation[11666:3438344] 当前线程是—{number = 1, name = main}

NSBlockOperation的使用:
实例化方法:+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
添加操作:- (void)addExecutionBlock:(void (^)(void))block;
当使用NSBlockOperation封装操作时候,创建对象时候添加的操作一定在主线程中运行,而在后来通过addExecutionBlock方法添加的操作,我在IPhone6S上的测试是:前两个添加的操作在主线程中执行。
eg:

- (void)testBlockOperation{    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{        // 在主线程中打印        NSLog(@"创建时候的打印---%@",[NSThread currentThread]);    }];    //  添加额外的任务时候,在子线程中执行    [blockOperation addExecutionBlock:^{        NSLog(@"添加时候的打印1---%@",[NSThread currentThread]);    }];    [blockOperation addExecutionBlock:^{        NSLog(@"添加时候的打印2---%@",[NSThread currentThread]);    }];    [blockOperation addExecutionBlock:^{        NSLog(@"添加时候的打印3---%@",[NSThread currentThread]);    }];    [blockOperation addExecutionBlock:^{        NSLog(@"添加时候的打印4---%@",[NSThread currentThread]);    }];    [blockOperation addExecutionBlock:^{        NSLog(@"添加时候的打印5---%@",[NSThread currentThread]);    }];    [blockOperation start];}

运行结果:
2016-03-18 14:13:24.631 ScanboxOperation[42808:11565184] 创建时候的打印—{number = 1, name = main}
2016-03-18 14:13:24.631 ScanboxOperation[42808:11565184] 添加时候的打印1—{number = 1, name = main}
2016-03-18 14:13:24.631 ScanboxOperation[42808:11565184] 添加时候的打印3—{number = 1, name = main}
2016-03-18 14:13:24.631 ScanboxOperation[42808:11565631] 添加时候的打印2—{number = 6, name = (null)}
2016-03-18 14:13:24.632 ScanboxOperation[42808:11565184] 添加时候的打印4—{number = 1, name = main}
2016-03-18 14:13:24.632 ScanboxOperation[42808:11565631] 添加时候的打印5—{number = 6, name = (null)}
结论:创建时候添加的操作一定是在主线程中执行,其后来添加的操作不一定在子线程中执行。

NSOperationQueue的使用:
eg:

- (void)invocationSel1{    NSLog(@"11当前线程是---%@",[NSThread currentThread]);}- (void)invocationSel2{    NSLog(@"22当前线程是---%@",[NSThread currentThread]);}- (void)testOperationQueue{    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];    // 创建NSInvocationOperation对象    NSInvocationOperation *invocationOperation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel1) object:nil];    NSInvocationOperation *invocationOperation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel2) object:nil];    // 创建NSBlockOperation    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"boockOperation3------%@",[NSThread currentThread]);    }];    [blockOperation3 addExecutionBlock:^{        NSLog(@"add operation1 to boockOperation3------%@",[NSThread currentThread]);    }];    [blockOperation3 addExecutionBlock:^{        NSLog(@"add operation2 to boockOperation3------%@",[NSThread currentThread]);    }];    NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"boockOperation4------%@",[NSThread currentThread]);    }];    [operationQueue addOperation:invocationOperation1];    [operationQueue addOperation:invocationOperation2];    [operationQueue addOperation:blockOperation3];    [operationQueue addOperation:blockOperation4];}

运行结果:
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573361] 22当前线程是—{number = 5, name = (null)}
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573362] 11当前线程是—{number = 4, name = (null)}
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573360] boockOperation4——{number = 2, name = (null)}
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573363] boockOperation3——{number = 3, name = (null)}
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573361] add operation1 to boockOperation3——{number = 5, name = (null)}
2016-03-18 14:24:38.651 ScanboxOperation[43082:11573362] add operation2 to boockOperation3——{number = 4, name = (null)}

操作依赖:
NSOperation之间可以设置依赖来保证执行顺序。比如一定要在操作A执行完之后再执行操作B:[opeartionB addDependency:operationA]

eg:

- (void)testDependency{    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];    NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"blockOperation1-------%@",[NSThread currentThread]);    }];    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"blockOperation2-------%@",[NSThread currentThread]);    }];    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"blockOperation3-------%@",[NSThread currentThread]);    }];    NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{            NSLog(@"blockOperation4-------%@",[NSThread currentThread]);    }];    NSBlockOperation *blockOperation5 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"blockOperation5-------%@",[NSThread currentThread]);    }];    blockOperation5.completionBlock = ^{        NSLog(@"blockOperation5--执行完毕-----%@",[NSThread currentThread]);    };    [blockOperation3 addDependency:blockOperation1];    [blockOperation1 addDependency:blockOperation2];    [blockOperation2 addDependency:blockOperation4];    [blockOperation4 addDependency:blockOperation5];    // 执行顺序:5、4、2、1、3    [operationQueue addOperation:blockOperation1];    [operationQueue addOperation:blockOperation2];    [operationQueue addOperation:blockOperation3];    [operationQueue addOperation:blockOperation4];    [operationQueue addOperation:blockOperation5];}

运行结果:
2016-03-18 14:49:33.048 ScanboxOperation[43638:11590189] blockOperation5——-{number = 2, name = (null)}
2016-03-18 14:49:33.049 ScanboxOperation[43638:11590191] blockOperation5–执行完毕—–{number = 3, name = (null)}
2016-03-18 14:49:33.049 ScanboxOperation[43638:11590189] blockOperation4——-{number = 2, name = (null)}
2016-03-18 14:49:33.049 ScanboxOperation[43638:11590189] blockOperation2——-{number = 2, name = (null)}
2016-03-18 14:49:33.049 ScanboxOperation[43638:11590189] blockOperation1——-{number = 2, name = (null)}
2016-03-18 14:49:33.050 ScanboxOperation[43638:11590189] blockOperation3——-{number = 2, name = (null)}

0 0