本文主要介绍iOS中多线程的实现方案,本文需要有iOS基础的同学观看,如果有什么问题欢迎留言联系。
iOS中多线程的实现方案有4中,如图:
上图简单明了的介绍了4种方式的优缺点,下面我们主要从第二种方式NSThread开始。
1、NSThread
(1)创建线程的方式有三种
/*
1、需要手动开启线程
开启了子线程
*/
NSThread *firstThread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@[@"firstThread"]];
//线程开始(需要手动开启)
firstThread.name = @"first";
[firstThread start];
/*
2、不需要手动开启线程
开启了子线程
*/
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"secondThread"];
/*
3、隐市创建方式
开启子线程
*/
[self performSelectorInBackground:@selector(run:) withObject:@[@"performSelectorInBackground"]];
三种方式的运行结果
2016-03-04 10:48:56.373 多线程demo[2307:48372] (
firstThread
)-----<NSThread: 0x7fc3cac1b170>{number = 2, name = first}
2016-03-04 10:48:56.373 多线程demo[2307:48373] secondThread-----<NSThread: 0x7fc3cad05b80>{number = 3, name = (null)}
2016-03-04 10:48:56.373 多线程demo[2307:48374] (
performSelectorInBackground
)-----<NSThread: 0x7fc3cad10770>{number = 4, name = (null)}
从运行结果可以看出,每种方式都开启了子线程。
(2)其他的隐式方式
2、GCD(重要),自动管理内存,C语言方法
两种队列:串行队列和并发队列
两个函数:同步函数dispatch_sync和异步函数dispatch_async(决定了要不要开启新的线程)
队列与函数结合使用的不同情况:
(1)全局并发队列添加到异步函数中
1、用dispatch_async异步函数往并发队列中添加任务—————》同时开启了3个子线程
- (void)asyncGlobalQueue
{
// 获取一个全局的默认优先级的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 总结: 同时开启了3个线程
}
执行结果:
2016-03-04 11:24:58.042 多线程demo[2413:66032] ----下载图片1-----<NSThread: 0x7fe798e38510>{number = 2, name = (null)}
2016-03-04 11:24:58.042 多线程demo[2413:66033] ----下载图片3-----<NSThread: 0x7fe798e03720>{number = 4, name = (null)}
2016-03-04 11:24:58.042 多线程demo[2413:66031] ----下载图片2-----<NSThread: 0x7fe798f0b4b0>{number = 3, name = (null)}
(2)串行队列添加到异步函数中,开启了一条子线程
2、用dispatch_async异步函数 往串行队列中添加任务————————》只开了一个线程执行任务
- (void)asyncSerialQueue
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 总结: 只开1个线程执行任务
}
执行结果:
2016-03-04 11:26:32.063 多线程demo[2426:67047] ----下载图片1-----<NSThread: 0x7f8dab521ff0>{number = 2, name = (null)}
2016-03-04 11:26:32.064 多线程demo[2426:67047] ----下载图片2-----<NSThread: 0x7f8dab521ff0>{number = 2, name = (null)}
2016-03-04 11:26:32.065 多线程demo[2426:67047] ----下载图片3-----<NSThread: 0x7f8dab521ff0>{number = 2, name = (null)}
(3)用dispatch_sync同步函数往并发队列中添加任务,不会开启新的线程,并发队列失去了并发的功能
3、用dispatch_sync同步函数往并发队列中添加任务—————》不会开启新的线程,并发队列失去了并发的功能
- (void)syncGlobalQueue
{
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.添加任务到队列中 执行
dispatch_sync(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 总结: 不会开启新的线程, 并发队列失去了并发的功能
}
执行结果
2016-03-04 11:31:42.592 多线程demo[2456:69917] ----下载图片1-----<NSThread: 0x7fe00a402310>{number = 1, name = main}
2016-03-04 11:31:42.592 多线程demo[2456:69917] ----下载图片2-----<NSThread: 0x7fe00a402310>{number = 1, name = main}
2016-03-04 11:31:42.593 多线程demo[2456:69917] ----下载图片3-----<NSThread: 0x7fe00a402310>{number = 1, name = main}
(4)同步函数执行串行队列,不会开启子线程
4、 用dispatch_sync同步函数往串行列中添加任务—————》不会开启新的线程
- (void)syncSerialQueue
{
// 1.创建串行队列(串行队列)
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
// 2.添加任务到队列中 执行
dispatch_sync(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 3.释放资源
// dispatch_release(queue); // MRC(非ARC)
// 总结: 不会开启新的线程
}
执行结果
2016-03-04 11:30:40.115 多线程demo[2443:69121] ----下载图片1-----<NSThread: 0x7fc5125086d0>{number = 1, name = main}
2016-03-04 11:30:40.116 多线程demo[2443:69121] ----下载图片2-----<NSThread: 0x7fc5125086d0>{number = 1, name = main}
2016-03-04 11:30:40.116 多线程demo[2443:69121] ----下载图片3-----<NSThread: 0x7fc5125086d0>{number = 1, name = main}
(5)用dispatch_sync同步函数, 在主线程中往主队列中添加任务 : 任务无法往下执行,产生死锁(死循环)
5、 用dispatch_sync同步函数, 在主线程中往主队列中添加任务 : 任务无法往下执行———————>产生死锁(死循环)
- (void)syncMainQueue
{
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加任务到队列中 执行
dispatch_sync(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
// dispatch_sync(queue, ^{
// NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
// });
// dispatch_sync(queue, ^{
// NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
// });
}
(6)异步函数, 在主线程中往主队列中添加任务
6、 使用dispatch_async异步函数, 在主线程中往主队列中添加任务
特殊情况:异步函数的任务添加在主队列中(往主队列中添加一个同步任务), 任务在主线程执行,不会开启新的线程
- (void)asyncMainQueue
{
// 1.获得主队列(串行队列)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
}
执行结果
2016-03-04 11:35:42.321 多线程demo[2485:72362] ----下载图片1-----<NSThread: 0x7f82fa407d90>{number = 1, name = main}
(7)异步函数在全局并发队列中执行group中的任务,同步函数不能执行group中的任务
//异步函数在全局并发队列中执行group中的任务
-(void)asyncGlobalQueueGroup
{
//获取到全局并发队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, global_queue, ^{
for (int i = 0; i < 100; i ++) {
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
} });
dispatch_group_async(group, global_queue, ^{
for (int i = 0; i < 100; i ++) {
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
}
});
//结果:在异步线程中并发执行group中的任务
}
3、NSOperation
NSOperation是对GCD的封装,将GCD封装成oc对象方便使用,NSOperation是抽象类,不能直接创建NSOperation的对象和调用对象方法,它有两个子类NSBlockOperation和NSInvocationOperation,使用时要用这两个方法来创建opreation对象、调用方法。
使用NSBlockOperation和NSInvocationOperation将需要的任务进行封装,
-(void)operation
{
//1、封装任务
NSBlockOperation *blockOpreation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 100; i ++) {
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
} }];
[blockOpreation addExecutionBlock:^{
for (int i = 0; i < 100; i ++) {
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
}
}];
//封装任务
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run:) object:@[@"invocationOperation"]];
//在blockOpreation中添加需要执行的任务
//设置blockOpreation中任务的优先级为最高
blockOpreation.queuePriority = NSOperationQueuePriorityVeryHigh;
// NSInvocation *invovation = [[NSInvocation alloc]ini]
// NSInvocationOperation *invoOpt = [[NSInvocationOperation alloc]initWithInvocation:<#(nonnull NSInvocation *)#>]
//2、创建队列
NSOperationQueue *optQueue = [[NSOperationQueue alloc]init];
//最大并发数
optQueue.maxConcurrentOperationCount = 1;
//添加依赖,当blockOpreation中的任务执行完毕在执行invocationOperation中的任务
[invocationOperation addDependency:blockOpreation];
//将任务添加到队列中自动执行
[optQueue addOperations:@[blockOpreation,invocationOperation] waitUntilFinished:YES];
}