自学内容网 自学内容网

iOS ------ 多线程 pthread,NSThread

一,pthread

1.1pthread简介

pthread是一套通用的多线程的API,可以在Unix/Linux/Windows等系统跨平台使用,使用C语言编写,需要程序员自己管理线程的生命周期,使用难度大。

1.2pthread使用方法

1,首先要包含头文件#import<pthread.h>
2,其次要创建线程,并开启线程任务

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建线程,定义一个pthread类型变量,为指向线程的指针
    pthread_t thread;
    //定义一个OC对象用于传递参数
    NSObject* obj = [[NSObject alloc] init];
    NSLog(@"objc:%p", obj);
    //开启线程:执行任务
    int result  = pthread_create(&thread, NULL, run, (__bridge void*)(obj));
    if (result == 0) {
        NSLog(@"创建线程成功");
    } else {
        NSLog(@"创建线程失败");
    }
    //设置子线程的状态为detached,该线程结束自动释放所有资源
    pthread_detach(thread);
    NSLog(@"%@", [NSThread currentThread]);
    // Do any additional setup after loading the view.
}
void * run(void* param) {
    NSLog(@"%@ param: %p", [NSThread currentThread], param);
    NSObject* instance = (__bridge NSObject*)parm;
    [instance 调用方法]return NULL;
}

上面将OC对象包装在符合签名的C函数中。
在创建线程时,我们将OC对象桥接为 void * 类型传递给 pthread_create。使用“桥接”(bridge)来处理指针类型转换是因为OC与C语言在内存管理和类型系统上存在差异。如果不使用桥接,ARC 可能认为对象不再需要管理,导致对象在转换后可能被释放。使用 __bridge 明确告诉 ARC 不要改变引用计数。

在函数中使用使用参数对象,调用他的方法。也需要使用桥接。如果没用使用桥接ARC 可能认为这是一个新的对象,需要增加引用计数。使用 __bridge 明确告诉 ARC 这是一个已有对象,不要改变引用计数。

pthread_create 是一个标准的 C 语言函数,用于创建一个新线程。它的函数签名如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
  • 第一个参数 pthread_t *thread:指向线程标识符的指针。
  • 第二个参数 const pthread_attr_t *attr:指向线程属性对象的指针。如果为 NULL,则使用默认属性。
  • 第三个参数 void *(*start_routine) (void *):线程启动函数的指针。
  • 第四个参数 void *arg:传递给线程启动函数的参数。

1.3 pthread 其他相关方法

  • pthread_create() 创建一个线程
  • pthread_exit() 终止当前线程
  • pthread_cancel() 中断另外一个线程的运行
  • pthread_join() 阻塞当前的线程,直到另外一个线程运行结束
  • pthread_attr_init() 初始化线程的属性
  • pthread_attr_setdetachstate() 设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
  • pthread_attr_getdetachstate() 获取脱离状态的属性
  • pthread_attr_destroy() 删除线程的属性
  • pthread_kill() 向线程发送一个信号

二,NSThead

NSThread 是苹果官方提供的,使用起来比 pthread 更加面向对象,简单易用,可以直接操作线程对象。不过也需要需要程序员自己管理线程的生命周期(主要是创建),我们在开发的过程中偶尔使用 NSThread。比如我们会经常调用[NSThread currentThread]来显示当前的进程信息.

2.1,创建,启动线程

  • 先创建线程,在启动线程
// 1. 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 2. 启动线程
[thread start];    // 线程一启动,就会在线程thread中执行self的run方法

// 新线程调用方法,里边为需要执行的任务
- (void)run {
     NSLog(@"%@", [NSThread currentThread]);
}
  • 创建线程后自动启动线程
// 1. 创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

// 新线程调用方法,里边为需要执行的任务
- (void)run {
     NSLog(@"%@", [NSThread currentThread]);
}
  • 隐式创建并启动线程
    这段代码用于在后台线程中异步执行实例方法 run。这种方法常用于执行需要较长时间完成的任务,而不阻塞主线程的操作
// 1. 隐式创建并启动线程
[self performSelectorInBackground:@selector(run) withObject:nil];

// 新线程调用方法,里边为需要执行的任务
- (void)run {
     NSLog(@"%@", [NSThread currentThread]);
}

2.2,线程相关方法

// 获得主线程
+ (NSThread *)mainThread;    

// 判断是否为主线程(对象方法)
- (BOOL)isMainThread;

// 判断是否为主线程(类方法)
+ (BOOL)isMainThread;    

// 获得当前线程
NSThread *current = [NSThread currentThread];

// 线程的名字——setter方法
- (void)setName:(NSString *)n;    

// 线程的名字——getter方法
- (NSString *)name;    

2.3线程状态控制方法

  • 启动线程方法
- (void)start;
// 线程进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
  • 阻塞(停止)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 线程进入阻塞状态
  • 强制停止线程
+ (void)exit;
// 线程进入死亡状态

2.4,线程之间的通信
在开发中,我们通常会在子线程进行耗时操作,操作结束后在回到主线程去刷新UI,这就涉及到子线程和主线程之间的通信。官方关于 NSThread 的线程间通信的方法。

// 在主线程上执行操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;
  // equivalent to the first method with kCFRunLoopCommonModes

// 在指定线程上执行操作
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

// 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

一个下载图片的例子,先开启一个子线程,在子线程中下载图片。再回到主线程刷新 UI,将图片展示在 UIImageView 中。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageView = [[UIImageView alloc] init];
    self.imageView.frame = CGRectMake(100, 100, 100, 100);
    [self.view addSubview:self.imageView];
     [self downloadImageOnSubThread];
}
- (void)downloadImageOnSubThread {
    //在创建的子线程调用downloadImage下载图片
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
- (void)downloadImage {
    NSLog(@"current thread -- %@", [NSThread currentThread]);
    NSURL* imageUrl = [NSURL URLWithString:@"https://ysc-demo-1254961422.file.myqcloud.com/YSC-phread-NSThread-demo-icon.jpg"];
    
    //从imageURL中读取图片(下载图片)耗时操作
    NSData* imageData = [NSData dataWithContentsOfURL:imageUrl];
    //痛过二进制data创建image
    UIImage* image = [UIImage imageWithData:imageData];
    
    //回到主线程进行图片的赋值和页面的刷新
    [self performSelectorOnMainThread:@selector(refreshOnMainThread:) withObject:image waitUntilDone:YES];
}
- (void)refreshOnMainThread:(UIImage*) image {
    NSLog(@"current thread -- %@", [NSThread currentThread]);
    
    //将图片复制到imageView上
    self.imageView.image = image;
}

原文地址:https://blog.csdn.net/m0_73974056/article/details/139220836

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!