多线程之pthread的简单使用

来源:互联网 发布:jquery 表单数据 编辑:程序博客网 时间:2024/05/22 14:21

  在iOS开发中,实现多线程的技术主要有四种,分别是pthread、NSThread、GCD和NSOperation。它们的技术特点如下表所示:


在iOS开发中多线程技术实现的方式.png

  在iOS中,一个应用程序运行后,默认会开启一条线程,我们称为“主线程”或“UI线程”。其作用主要是用来显示或者刷新UI界面、处理UI事件(比如说滚动、点击和拖拽等)。通常情况下,不能将耗时操作放在主线程当中,否则会引起应用卡顿,造成不好的用户体验。

  下面我们就来演示一下,如果将耗时操作放在主线程当中执行会发生什么状况。新建一个工程,往Main.storyboard文件中的控制器里拖入一个按钮,再拖入一个UIDatePicker控件,然后运行程序,先看一下正常情况下,程序运行的结果是怎样的:


正常情况下,UIDatePicker的滚动情况.gif

  从图中可以看出,正常情况下,UIDatePicker的滚动是十分流畅的。再来看一下,如果将比较耗时的操作放在主线程中执行,UIDatePicker的滚动情况:

// MARK:- 点击按钮以后执行相关操作- (IBAction)btnClick {    // 打印10000次    for (int i = 0; i < 10000; i++) {        NSLog(@"%d:%@", i, [NSThread currentThread]);    }}

  运行程序,然后点击屏幕上的按钮以后,再来尝试滚动UIDatePicker,看看会发生什么情况:


在主线程中执行耗时操作时程序运行的情况.gif

  从上面的图中,我们可以清楚的看到,当我们点击按钮,也就是执行耗时操作之前,UIDatePicker控件是可以正常滚动的。但是,当我们点击按钮执行耗时操作时,UIDatePicker就不能正常滚动了。只有当打印结束,也就是耗时操作执行完毕以后,UIDatePicker才又重新恢复正常。

  从上面的演示可知,耗时操作一般不宜放在主线程中执行,而应当重新开启一条子线程,在子线程中执行这类代码。下面,我们就演示一下,如何使用pthread来开启子线程:

// MARK:- 点击按钮以后执行相关操作- (IBAction)btnClick {    // 创建线程对象    pthread_t thread;    // 创建线程    pthread_create(&thread, NULL, createChildThread, NULL);    /**     *  pthread_create(<#pthread_t  _Nullable *restrict _Nonnull#>, <#const pthread_attr_t *restrict _Nullable#>, <#void * _Nullable (* _Nonnull)(void * _Nullable)#>, <#void *restrict _Nullable#>)     *  <#pthread_t  _Nullable *restrict _Nonnull#> :表示传入线程对象的地址,不能为空;     *  <#const pthread_attr_t *restrict _Nullable#>:表示线程的一些属性,可以为空;     *  <#void * _Nullable (* _Nonnull)(void * _Nullable)#>:表示指向某个函数的指针,这个不能为空;     *  <#void *restrict _Nullable#>:表示函数需要接收的参数,也可以为空。     */}// 再子线程中执行耗时操作void * createChildThread(void * param) {    // 在子线程中来一个耗时操作    for (int i = 0; i < 100000; i++) {        NSLog(@"%zd:%@", i, [NSThread currentThread]);    }    return NULL;}

  我们先来运行程序,看一下把耗时操作放在子线程中执行,UIDatePicker能是否正常滚动,再来解释上面的代码:


把耗时操作放在子线程执行以后,UIDatePicker的滚动情况.gif

  从上面的图中,我们可以看出,虽然控制台一直在打印消息(耗时操作一直在执行),但是UIDatePicker却一直可以正常滚动。这说明,当我们把耗时操作放在子线程中执行时,是不影响我们UI界面操作的。

  下面,我们来解释一下上面的代码。要使用pthread,首先应该包含该它的头文件。在使用pthread_t创建完pthread对象之后,再利用pthread_create ()函数创建子线程。在创建子线程的过程中,pthread_create ()函数需要传入4个参数:

pthread_t _Nullable *restrict _Nonnull:表示传入线程对象的地址,不能为空;
const pthread_attr_t *restrict _Nullable:表示线程的一些属性,可以为空;
void * _Nullable (* _Nonnull)(void * _Nullable):表示指向某个函数的指针,这个不能为空;
void *restrict _Nullable:表示函数需要接收的参数,也可以为空。

  在这4个参数里面,_Nullable表示参数可以为空,_Nonnull表示参数不能为空。先来说第一个参数,它要求传入一个pthread_t对象的地址,是不能为空的;第二个参数,它用来描述线程的一些属性的,可以为空;第三个参数,它要求传入一个指向函数的指针。也就是说,它要求传入一个返回值为空、参数可以为空的函数的地址,并且,这个地址是必须要传的(因为,我们要把耗时操作的代码放在这个函数当中);第四个参数,它表示前面第三个参数中的函数的参数,有点绕口,其实就是我们程序当中createChildThread ()这个函数的参数。

  由于pthread在iOS开发中实际用到的场合非常少,所以我们只需要做一个简单的了解就可以了,不需要做深入的学习。在后面的笔记中,我们会逐步接触比较常用的NSThread、GCD和NSOperation的相关知识,pthread的笔记到这里就告一段落。