多线程:Pthread、NSThread、GCD、NSOperation的使用

多线程一直是在开发中经常使用的,实现多线程的方式总共有四种,分别是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

终于码完了这篇文章,希望对小伙伴们有所帮助~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容