多线程一直是在开发中经常使用的,实现多线程的方式总共有四种,分别是Pthread、NSThread、GCD、NSOperation,通过这篇文章记录一下每种方式常用的方法。
1.Pthread
Pthread基于C语言框架来实现的,所以在iOS开发中一般不使用。下面写一个demo来简单的看一下Pthread是如何使用的。
- (void) pthreadClick {
NSLog(@"pthread在主线程中执行");
pthread_t pthread;
pthread_create(&pthread, NULL, run, NULL);
}
//C语言方法
void *run (void *data){
NSLog(@"pthread在子线程中执行");
for (int i=0; i<10; i++) {
NSLog(@"%d",i);
}
return NULL;
}
2.NSThread的使用
NSThread的初始化方法有三种:
1)通过alloc init方式,需要通过手动调用start方法启动
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(nsthreadRun) object:nil];
//设置线程名字
thread.name = @"NSThread name1";
//设置线程优先级(值高的优先执行)
thread.threadPriority = 0.3;
[thread start];
2)通过detachNewThreadSelector创建并执行线程,该种方式不需要手动调用start方法
[NSThread detachNewThreadSelector:@selector(nsthreadRun) toTarget:self withObject:nil];
3)通过performSelectorInBackground方式创建
[self performSelectorInBackground:@selector(nsthreadRun) withObject:nil];
使用NSThread实现从子线程回到主线程
[self performSelectorOnMainThread:@selector(nsthreadRunMainThread) withObject:nil waitUntilDone:YES];
//回到主线程进行相应的操作
-(void)nsthreadRunMainThread{
NSLog(@"NSThread回到主线程执行");
}
在nsthreadRunMainThread方法里做回到主线程之后的相应操作
3.GCD的使用
1)dispatch_get_global_queue 的使用
dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>),第一个参数代表子线程的优先级,对应的值有DISPATCH_QUEUE_PRIORITY_HIGH(优先级高),DISPATCH_QUEUE_PRIORITY_LOW(优先级低),DISPATCH_QUEUE_PRIORITY_DEFAULT(优先级处于HEIGH和LOW之间),举例如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSLog(@"task1 start");
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
NSLog((@"task1 end"));
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"task2 start");
[NSThread sleepForTimeInterval:2];
NSLog((@"task2 end"));
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"task3 start");
[NSThread sleepForTimeInterval:2];
NSLog((@"task3 end"));
});
在上面的三个异步操作中,优先级从高到低分别是task2,task3,task1,所以执行的开始顺序也是 task2,task3,task1。
dispatch_get_global_queue是由系统提供的一个全局并行队列,那么如何自己创建队列呢?使用dispatch_queue。
2)dispatch_queue的使用
创建队列的方法是:
dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>),第一个参数作为queue的唯一标识,第二个参数代表创建的queue是并行(DISPATCH_QUEUE_CONCURRENT)的还是串行(DISPATCH_QUEUE_SERIAL,NULL)的
创建dispatch_queue,以创建并行队列为例:
dispatch_queue_t queue = dispatch_queue_create("gcd_queue", DISPATCH_QUEUE_CONCURRENT);
自定义创建的队列可以创建串行和并行两种队列,第二个参数如果是DISPATCH_QUEUE_SERIAL或者NULL则创建的是串行队列,如果是DISPATCH_QUEUE_CONCURRENT则创建的是并行队列
3)dispatch_group的使用
有的时候会遇到多个异步操作都完成之后再进行下一步的操作,那么如何断定多个异步操作都完成了呢?这个时候就用到了dispatch_group
dispatch_group的创建和使用
创建dispatch_group
dispatch_group_t group = dispatch_group_create();
创建dispatch_queue,这里创建的是并行队列
dispatch_queue_t queue = dispatch_queue_create("gcd_queue", DISPATCH_QUEUE_CONCURRENT);
创建异步操作
//异步操作一
dispatch_group_async(group, queue, ^{
NSLog(@"task start 1");
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
NSLog((@"task end 1"));
});
//异步操作二
dispatch_group_async(group, queue, ^{
NSLog(@"task start 2");
[NSThread sleepForTimeInterval:2];
NSLog((@"task end 2"));
});
所有的异步操作完成之后,接收通知,进一步做处理
dispatch_group_notify(group, queue, ^{
//回调回来的数据在异步线程中
NSLog(@"all task over");
//返回主线程
dispatch_async(dispatch_get_main_queue(), ^{
//返回主线程刷新UI
NSLog(@"返回主线程刷新UI");
});
});
我们可以看到上面的异步操作里面是同步操作,如果在异步操作里面仍然是异步操作,这个时候执行完成第一层异步操作之后,就收到通知说异步操作都执行完成,但其实第二层异步操作还没有执行完成,这显然不是想要的结果,这个时候应该用什么办法解决这种问题?
要解决这个问题可以使用dispatch_group_enter(group)和dispatch_group_leave(group)这两个方法。举例如下:
dispatch_group_enter(group);
[self sendRequest:^{
NSLog(@"request 1 done");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self sendRequest2:^{
NSLog(@"request 2 done");
dispatch_group_leave(group);
}];
这两个方法可以理解为进入子线程和离开子线程,当所有的异步操作都离开子线程之后就可以发送通知异步操作全部执行完毕,完美解决问题
4.NSOperation的使用
NSOperation是一个抽象的类,不能直接实例化对象,只能使用其子类进行对象的创建,系统提供的子类有NSInvocationOperation和NSBlockOperation。
1)使用NSInvocationOperation和NSBlockOperation创建,这两种方法需要手动调用start启动,且该方法并没有创建一个新的线程,仍然处于当前线程,会阻塞当前线程
使用NSInvocationOperation创建
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationRun) object:nil];
[operation start];
使用NSBlockOperation创建
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
//仍然处于当前线程中执行
NSLog(@"NSOperation 在当前线程中执行");
}];
[blockOperation start];
为了节省资源,提高性能,小伙伴们更倾向于使用异步操作,使用NSOperation如何创建异步操作呢?这个时候就用到了NSOperationQueue
2)NSOperationQueue的使用
创建NSOperationQueue对象
NSOperationQueue queue = [[NSOperationQueue alloc]init];
将Operation对象添加到queue队列中,此时不需要手动调用start方法
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationRun) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
}];
[queue addOperation:operation];
[queue addOperation:blockOperation];
如果同时有多个异步操作,但是不想一次执行那么多,可以设置最大并发量
queue.maxConcurrentOperationCount = 2;
有时候会出现一个操作依赖另一个操作返回的值,此时可以使用addDependency方法
operation addDependency:blockOperation
3)自定义继承NSOperation类的实现
自定义CustomerOperation类,继承于NSOperation,重写main方法
//.h文件
#import@interface CustomerOperation : NSOperation
- (instancetype)initWithName:(NSString *)name;
@end
//.m文件
#import "CustomerOperation.h"
@interface CustomerOperation ()
@property (copy, nonatomic) NSString *operName;
@property (assign, nonatomic) BOOL over;
@end
@implementation CustomerOperation
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
self.operName = name;
}
return self;
}
-(void)main{
//异步请求,需要判断异步请求是否完成,在这里使用RunLoop实现
//如果是同步操作,不需要使用RunLoop
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if (self.cancelled) {
return ;
}
NSLog(@"%@",self.operName);
self.over = YES;
});
while (!self.over && !self.cancelled) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
调用位置代码如下:
CustomerOperation *customerOperA = [[CustomerOperation alloc]initWithName:@"cutomerA"];
CustomerOperation *customerOperB = [[CustomerOperation alloc]initWithName:@"cutomerB"];
CustomerOperation *customerOperC = [[CustomerOperation alloc]initWithName:@"cutomerC"];
CustomerOperation *customerOperD = [[CustomerOperation alloc]initWithName:@"cutomerD"];
[customerOperD addDependency:customerOperA];
[customerOperA addDependency:customerOperC];
[customerOperC addDependency:customerOperB];
[queue addOperation:customerOperA];
[queue addOperation:customerOperB];
[queue addOperation:customerOperC];
[queue addOperation:customerOperD];
此时调用的顺序是B、C、A、D
终于码完了这篇文章,希望对小伙伴们有所帮助~~
网友评论