在iOS中 多线程技术有三种 NSThread GCD NSOperation 这三种方式的抽象层度依次有低到高 ,抽象层度越高 也就用着越方便 也是苹果官方推荐的 下面我们依次介绍三种多线程技术
1.NSThread
优点:有点量级比较轻
缺点:需要我们自己去管理线程的整个生命周期,使用起来比较麻烦
在开发工程中 这个方式我们很少用到
2.GCD 全称(Grand Central Dispatch)
GCD 是一种完全用C语言编写的iOS多线程技术
介绍完三种多线程技术,下面我们就看一下它们是真么用的吧
NSThread:这种多线程技术我们在开发过程中很少使用,所及这里不做过多介绍,简单的介绍一下
@interface HMViewController ()
- (IBAction)btnClick;
@end
@implementation HMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnClick {
// 1.获得当前的线程
NSThread *current = [NSThread currentThread];
NSLog(@"btnClick---%@", current);
// NSThread *main = [NSThread mainThread];
// NSLog(@"btnClick---%@", main);
// 2.执行一些耗时操作 : 创建一条子线程
[self threadCreate];
}
- (void)run:(NSString *)param
{
NSThread *current = [NSThread currentThread];
for (int i = 0; i<10000; i++) {
NSLog(@"%@----run---%@", current, param);
}
}
/**
* NSThread的创建方式
* 隐式创建线程, 并且直接(自动)启动
*/
- (void)threadCreate3
{
// 在后台线程中执行 === 在子线程中执行
[self performSelectorInBackground:@selector(run:) withObject:@"abc参数"];
}
/**
* NSThread的创建方式
* 创建完线程直接(自动)启动
*/
- (void)threadCreate2
{
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是参数"];
}
/**
* NSThread的创建方式
* 1> 先创建初始化线程
* 2> start开启线程
*/
- (void)threadCreate
{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
thread.name = @"线程A";
// 开启线程
[thread start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
thread2.name = @"线程B";
// 开启线程
[thread2 start];
}
GCD
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *leftImage;
@property (weak, nonatomic) IBOutlet UIImageView *rightImage;
@property (weak, nonatomic) IBOutlet UIImageView *bigImage;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建一个同步队列 同步队列会按照先进先出的方法一个一个的串发执行任务
//第一个参数是队列的名称,给创建的同步对列添加一个名称
//注意这里是c语言语法不能用 “@”
//第二个参数是对列的属性,一般填写NULL
dispatch_queue_t queue = dispatch_queue_create("同步队列", NULL) ;
dispatch_queue_t queue2 = dispatch_queue_create("同步队列", NULL) ;
//获取主线程,主线程是一个同步队列
//特别注意,不能用串发执行的方法往主线程中添加任务
// dispatch_queue_t queue5 = dispatch_get_main_queue() ;
//创建一个异步队列 异步队列会吧所有任务取出同步执行
//第一个参数是队列的优先级 是给队列设置优先级
/**
DISPATCH_QUEUE_PRIORITY_HIGH 较高的优先级 最先执行
DISPATCH_QUEUE_PRIORITY_DEFAULT 系统默认的优先级 优先级第二
DISPATCH_QUEUE_PRIORITY_LOW 较低的优先级 优先级第三
DISPATCH_QUEUE_PRIORITY_BACKGROUND 这个优先级最低 最后执行
*/
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
//sync 串行执行 不允许创建线程
//async 并发执行,允许创建线程
//第一个参数是队列的名称
//第二个参数是要添加到队列中的事情
//将任务按照串行执行的方法一个一个的添加到同步对列queue中去, 不会创建线程,切任务是一个一个的执行
dispatch_sync(queue, ^{
NSLog(@"111111111111111%@",[NSThread currentThread]) ;
}) ;
dispatch_sync(queue, ^{
NSLog(@"555555555555555%@",[NSThread currentThread]) ;
}) ;
//将任务按照串行执行的方法一个一个的添加到异步对列queue1中去, 不会创建线程,任务是多个一起执行
dispatch_sync(queue1, ^{
NSLog(@"22222222222222%@",[NSThread currentThread]) ;
}) ;
//将任务按照并发执行行的方法一个一个的添加到同步对列queue中去, 会创建一个线程,多个任务会在这个创建的线程内一个一个的执行
dispatch_async(queue2, ^{
NSLog(@"33333333333333%@",[NSThread currentThread]) ;
}) ;
//将任务按照并发执行行的方法一个一个的添加到异步对列queue中去, 会创建多个线程,多个任务会在这个创建的线程内的执行一起执行
dispatch_async(queue3, ^{
NSLog(@"44444444444444444%@",[NSThread currentThread]) ;
}) ;
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 *NSEC_PER_SEC));
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread] ) ;
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@",[NSThread currentThread] ) ;
});
dispatch_group_t group = dispatch_group_create() ;
dispatch_group_async(group, queue, ^{
NSLog(@"6666666666666group") ;
}) ;
dispatch_group_async(group, queue1, ^{
NSLog(@"11111111111111group") ;
}) ;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"group") ;
}) ;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"啊哈哈哈哈") ;
});
dispatch_group_t group = dispatch_group_create() ;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
dispatch_queue_t queue1 = dispatch_queue_create("123", NULL) ;
__block UIImage * image = nil ;
__block UIImage * image1 = nil ;
dispatch_group_async(group, queue, ^{
NSString * string = @"http://f.hiphotos.baidu.com/image/pic/item/0eb30f2442a7d933fdd1619ba94bd11372f001d8.jpg" ;
NSURL * URL = [NSURL URLWithString:string] ;
NSData * data = [NSData dataWithContentsOfURL:URL] ;
image = [UIImage imageWithData:data] ;
NSLog(@"image1") ;
}) ;
dispatch_group_async(group, queue, ^{
NSString * string = @"http://h.hiphotos.baidu.com/image/pic/item/d058ccbf6c81800a7e38fce4b53533fa838b477e.jpg" ;
NSURL * URL = [NSURL URLWithString:string] ;
NSData * data = [NSData dataWithContentsOfURL:URL] ;
image1 = [UIImage imageWithData:data] ;
NSLog(@"imag2") ;
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
self.leftImage.image = image ;
self.rightImage.image = image1 ;
}) ;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
第二个例子
@interface HMViewController ()
@property (nonatomic, strong) NSThread *thread;
@end
@implementation HMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
self.thread.name = @"线程A";
}
- (void)test
{
NSLog(@"test - 开始 - %@", [NSThread currentThread].name);
// [NSThread sleepForTimeInterval:5]; // 阻塞状态
// NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5.0];
// [NSThread sleepUntilDate:date];
for (int i = 0; i<1000; i++) {
NSLog(@"test - %d - %@", i, [NSThread currentThread].name);
if (i == 50) {
[NSThread exit];
}
}
NSLog(@"test - 结束 - %@", [NSThread currentThread].name);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 开启线程
[self.thread start];
}
NSOperation 是一种基于GCD开发的技术,它突出了任务的概念,弱化线程的概念。起始NSOperation并不能进行进行多线程任务,而是他的子类方法进行的 一般情况下NSOperation需要与NSOperationQueue结合起来使用 NSOperation是通过子类NSBlockOperation、NSInvocationOperation和自定义的NSOperation的子类来实现,这里只对前两个做介绍,因为前两个基本上能满足我们开发的需求
NSOperation:
创建一个任务
//创建操作对象
NSInvocationOperation * operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil] ;
//添加到任务队列中去
NSOperationQueue * queue = [[NSOperationQueue alloc]init] ;
//把任务添加到任务队列中去
//注意这里需要天加到任务队列中去,要不然需要手动调用 [operation start] 方法开启线程,并且是不会开辟新线程的,无论起了多扫个任务
[queue addOperation:operation] ;
创建一个NSBlockOperation任务
//创建block任务 默认情况下是不会创建线程
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-----下载图片1-------%@",[NSThread currentThread]) ;
}] ;
// 注意 blockOperation 如果添加多任务即便是不放在 任务队列 中仍然开辟线程
[operation addExecutionBlock:^{
NSLog(@"-----下载图片2-------%@",[NSThread currentThread]) ;
}] ;
[operation addExecutionBlock:^{
NSLog(@"-----下载图片3-------%@",[NSThread currentThread]) ;
}] ;
[operation addExecutionBlock:^{
NSLog(@"-----下载图片4-------%@",[NSThread currentThread]) ;
}] ;
//开始线程
[operation start] ;
这样就开辟了三条线程 ,如果添加到OperationQueue,所有的任务都是在子线程内实现的,不在主线程中
NSOperationQueue的一些设置
设置最大的开启线程的数量
//设置最大并发线程的数量(不包括主线程),如果设置为3 包括主线程在内为4个
queue.maxConcurrentOperationCount = 3 ;//5以内
取消所有线程
//取消所有的线程
[queue cancelAllOperations] ;
暂停和开启所有线程
//暂停线程 YES 表示暂停
[queue setSuspended:YES] ;
//恢复线程 NO表示继续
[queue setSuspended:NO] ;
取消单个线程
[operation1 cancel] ;
设置现成的优先级,优先级越高,执行的可能性越大,被执行的次数越多
//设置优先级
/*
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
*/
[operation1 setQueuePriority:NSOperationQueuePriorityHigh] ;
设置线程的依赖关系
//操作依赖 operation2 做完后才能做 operation1
//可以在不同队列中添加依赖,但是不能相互依赖
[operation1 addDependency:operation2] ;
第三个例子
@interface HMViewController ()
/** 剩余票数 */
@property (nonatomic, assign) int leftTicketsCount;
@property (nonatomic, strong) NSThread *thread0;
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@end
@implementation HMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 默认有100张
self.leftTicketsCount = 100;
// 开启多条线程同时卖票
self.thread0 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread0.name = @"售票员 A";
// self.thread0.threadPriority = 0.0;
self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread1.name = @"售票员 B";
// self.thread1.threadPriority = 1.0;
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread2.name = @"售票员 C";
// self.thread2.threadPriority = 0.0;
}
/**
* 卖票
*/
- (void)saleTicket
{
while (1) {
@synchronized(self) { // 加锁(只能用一把锁)
// 1.先检查票数
int count = self.leftTicketsCount;
if (count > 0) {
// 暂停
// [NSThread sleepForTimeInterval:0.0002];
// 2.票数 - 1
self.leftTicketsCount = count - 1;
NSThread *current = [NSThread currentThread];
NSLog(@"%@ 卖了一张票, 剩余%d张票", current.name, self.leftTicketsCount);
} else {
// 退出线程
[NSThread exit];
}
} // 解锁
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.thread0 start];
[self.thread1 start];
[self.thread2 start];
}