iOS多线程(二):多线程实现方案(pthread、NSThread)

iOS中主要有四种实现多线程操作的方案,pthread、NSthread、GCD和NSOperation。前两个用得很少,基本不用,iOS代码中主要靠后面两个。但是后面两个实际上最终都是被“翻译”成pthread的方法来实现与系统交互的。

1、pthread

pthread可以说是一个万能膏药,是一套通用的多线程接口,可以在Linux/Unix/Windows/iOS等操作系统中跨平台使用,它是基于C语言的,而且需要程序员手动来管理线程的开启和销毁。

使用pthread需要引入头文件

#import <pthread.h>

来看一段pthread的使用代码:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    pthread_t thread;
    // 创建一个线程并自动执行
    int i = 10;
    pthread_create(&thread, NULL, start, &i);
    pthread_join(thread, NULL);
}

void *start(void *data){
    
    NSLog(@"%@", [NSThread currentThread]);
    int i = *(int *)data;
    printf("%d", i);
    return NULL;
}

通过pthread_create方法来创建一个线程,这个方法需要传四个参数。

  • 第一个参数是创建线程的地址
  • 第二个参数是配置线程的属性,如果设置为NULL,则为默认的属性配置。关于属性配置可以查看pThread_attr_t相关资料。
  • 第三个参数是线程运行的函数的指针。
  • 第四个参数是运行函数的参数

有些童鞋可能会在第三个参数中传参,我这边试了单个参数也是可以的。但是这样可能会导致不可知的问题,严格意义来说第三个参数只能传函数的地址。

如果需要传递函数参数,仅传一个参数时,可以按照如图代码所示直接在第四个参数中配置。但是如果需要传递多个参数,那么需要把多个参数放在一个结构体中,然后把结构体作为运行函数的参数来传递。

线程架构图

注:图片转自欧阳大哥:iOS线程生命周期的监控

pthread是iOS中其他多线程实现方式的基础,其他几种多线程实现方式都最终会转化成pthread的方法,只有pthread的方法才能与系统底层进行交互。更深入的交互过程还有待后续继续挖掘之后再来分享,也可参考上面大神的分解。

2、NSThread

NSThread是苹果官方提供的第一种多线程操作接口,基于Objective-C,方便iOS开发者使用,但是由于这个对象仍然需要手动来管理线程的生命周期,在开发中用得并不是很多。

2.1 创建线程

创建线程可以通过三种方式:

  • 通过init来添加一个运行方法,这种方式创建的线程需要使用start来手动的启动线程。@selector中的方法就是需要执行的代码。
// 创建线程(子线程)
    NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadCallFunc) object:nil];
    [myThread start];
  • 下面的两种方式是不需要手动开启线程,创建之后会自动执行start方法。其中iOS10.0以后新增的线程创建方式是通过Block来传递需要执行的代码。
 // Block方式,iOS10以后可用
    if (@available(iOS 10.0, *)) {
        [NSThread detachNewThreadWithBlock:^{
        }];
    } else {
        // Fallback on earlier versions
        // 创建并同时自动启动的方法(SEL)
        [NSThread detachNewThreadSelector:@selector(threadCallFunc) toTarget:self withObject:nil];
    }

2.2 NSThread的基本属性

要查看NSThread的属性和定义的方法,可以点开NSThread查看.h文件的声明。
主要有以下一些方法的使用,不过由于NSThread对于开发者也并不是那么友好,开发过程中很多时候我们基本用的比较多的只有[NSThread currentThread]来获取当前线程这个方法了。

 // 线程调用返回的栈地址的记录,实际返回的是虚拟地址,就是当前线程所在函数所使用的虚拟地址的的数组
    NSArray *array =  [NSThread callStackReturnAddresses];
    NSArray *array1 =  [NSThread callStackSymbols];
    NSLog(@"栈地址数组--%@", array);
    NSLog(@"栈地址符号数组--%@", array1);
    
    /**
     * NSThread中其他方法
     */
    // 取消线程
    [myThread cancel];
    // 设置和获取线程方法,自己创建的线程一般打印出来name字段是没有的,可以通过这个方法设置线程的名字
    [myThread setName:@"jc-thread"];
    NSString *threadName = [myThread name];
    NSLog(@"当前threadName:%@", threadName);
    // 获取当前线程
    [NSThread currentThread];
    // 获取主线程
    [NSThread mainThread];
    // 使当前线程休眠一段时间
    [NSThread sleepForTimeInterval:3.0f];
    // 使当前线程一直休眠直到某个时间
    [NSThread sleepUntilDate:[NSDate date]];
    
    // NSTread BOOL
    // 是否正在执行
    BOOL isExcute = myThread.isExecuting;
    // 是否已经取消
    BOOL isCanceled = myThread.isCancelled;
    // 是否已经结束
    BOOL isFinished = myThread.isFinished;
    NSLog(@"%d, %d, %d", isExcute, isCanceled, isFinished);

提一下callStackReturnAddress和callStackSymbols这两个方法。我们平时在调试bug的时候,遇到崩溃的问题时可能会被断到某个地址,或者包括我们查看友盟的一些崩溃日志时也可能会留意到一些看不懂的地址。这些地址和符号就是方法名通过特殊处理后的展示,分析问题的时候通过符号表,可以反过来推倒报错的方法名从而更快的找到问题。所以这两个方法可以与NSLog联合使用来跟踪线程的函数使用情况。

2.3 线程的优先级

在编写程序的过程中,不同的任务会有不同的优先级,为了满足这个需求,就存在着线程的优先级,通过优先级设置来告诉系统哪些任务需要立即执行,哪些任务可以缓一缓。

NSThread给线程订立了五个优先级,可以通过如下属性来设置子线程的优先级

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started

可以点击NSQualityOfService查看优先级结构体详情,他们的优先级顺序是NSQualityOfServiceUserInteractive> NSQualityOfServiceUserInitiated> NSQualityOfServiceDefault> NSQualityOfServiceUtility> NSQualityOfServiceBackground
未设置优先级时,优先级默认为NSQualityOfServiceDefault

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21, // 最高优先级,主要用于UI交互的操作,比如处理点击事件,绘制图像到屏幕上等
    NSQualityOfServiceUserInitiated = 0x19, // 次高优先级,主要用于需要立即执行的操作
    NSQualityOfServiceUtility = 0x11, // 普通优先级,主要用于不需要立即执行的操作
    NSQualityOfServiceBackground = 0x09, // 后台优先级,用于完全不紧急的操作
    NSQualityOfServiceDefault = -1 // 默认优先级,当没有设置优先级的时候,线程默认优先级
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

2.4 线程间通信

  • 指定当前线程执行操作,是继承自NSObject的方法。
// 指定当前线程执行操作
    [self performSelector:@selector(nsThreadComunications)];
    [self performSelector:@selector(nsThreadComunications) withObject:nil];
    [self performSelector:@selector(nsThreadComunications) withObject:nil afterDelay:1.0f];

  • 指定在主线程执行操作
  // 指定在主线程中执行操作
    [self performSelectorOnMainThread:@selector(nsThreadComunications) withObject:nil waitUntilDone:NO];
    // 结合runloop,唤醒主线程中的runloop
    [self performSelector:@selector(nsThreadComunications) withObject:nil afterDelay:1.0f inModes:@[NSRunLoopCommonModes]];
  • 当前在主线程时,指定在其他线程执行操作
  // 指定在子线程执行操作
    [self performSelector:@selector(nsThreadComunications) onThread:myThread withObject:nil waitUntilDone:NO];
    [self performSelectorInBackground:@selector(nsThreadComunications) withObject:nil];

参考博客:
pthread_attr_t参数详解
pthread_create函数的详细讲解
多线程概念

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

推荐阅读更多精彩内容