iOS多线程 之 pthread详解

  • pthread 是 POSIX 多线程开发框架,是跨平台的 C 语言框架,需要自己管理线程的创建销毁等操作。
  • pthread_t ,用于标识一个线程,不能单纯看成整数,通过头文件可以看到是_opaque_pthread_t 类型的结构体指针

使用

  • 引入pthread的头文件
#import <pthread/pthread.h>
  • 设置线程属性:pthread_attr_t
    1.1 初始化线程属性:
    pthread_attr_t pthreadAttr;
    pthread_attr_init(&pthreadAttr);
    
    1.2. 设置属性:
    • detachstate:表示该线程是否与进程中其他线程分离同步。

      /* 设置线程的分离状态
       *
       * 第一个参数:pthread_attr_t:线程属性对象,传地址
       * 第二个参数:线程的分离状态。
         PTHREAD_CREATE_DETACHED(分离线程):父线程在创建子线程之后,父线程不会去等待子线程结束再去运行自己接下来的程序。
         PTHREAD_CREATE_JOINABLE(非分离线程):父线程会等待子线程运行结束,才继续运行接下来的程序。
       */
      //注意的是如果当线程一旦处于PTHREAD_CREATE_DETACHED状态,那么线程的状态就无法再被修改了。
      //线程创建时默认设置为PTHREAD_CREATE_JOINABLE状态。
      //线程的分离状态也可以在线程创建之后再去设置:pthread_detach()
      pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_JOINABLE);
      
    • schedpolicy:线程的调度策略。

      /* 设置线程的调度策略
       *
       * 第一个参数:pthread_attr_t:线程属性对象,传地址
       * 第二个参数:线程的调度策略。
         SCHED_FIFO(实时调度策略,先进先出):它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。它意味着,使用**SCHED_FIFO**的可运行线程将一直抢占使用**SCHED_OTHER**的运行线程J。此外**SCHED_FIFO**是一个非分时的简单调度策略,当一个线程变成可运行状态,它将被追加到对应优先级队列的尾部((POSIX 1003.1)。当所有高优先级的线程终止或者阻塞时,它将被运行。对于相同优先级别的线程,按照简单的先进先运行的规则运行。我们考虑一种很坏的情况,如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用**SCHED_FIFO**的时候要小心处理相同级别线程的动作。
         SCHED_RR(实时调度策略,轮转法):**SCHED_RR**对**SCHED_FIFO**做出了一些增强功能。从实质上看,它还是**SCHED_FIFO**调用策略。它使用最大运行时间来限制当前进程的运行,当运行时间大于等于最大运行时间的时候,当前线程将被切换并放置于相同优先级队列的最后。这样做的好处是其他具有相同级别的线程能在“自私“线程下执行。
         SCHED_OTHER(分时调度策略,默认的):它是默认的线程分时调度策略,所有的线程的优先级别都是0,线程的调度是通过分时来完成的。简单地说,如果系统使用这种调度策略,程序将无法设置线程的优先级。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。
       */
      pthread_attr_setschedpolicy(&pthreadAttr, SCHED_OTHER);
      
    • schedparam:线程优先级的设定。(在设置线程的优先级时,调度策略不能为SCHED_OTHER,因为SCHED_OTHER无法设置线程的优先级)

      struct sched_param parm;
      parm.sched_priority = 0;
      /* 设置线程优先级
       *
       * 第一个参数:pthread_attr_t:线程属性对象,传地址
       * 第二个参数:sched_param:一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_attr_setschedparam()函数来改变,默认为0。
       */
      pthread_attr_setschedparam(&pthreadAttr, &parm);
      
    • inheritsched:设置线程是否继承调用者的线程属性。

      /* 设置线程的属性是否继承
       *
       * 第一个参数:pthread_attr_t:线程属性对象,传地址
       * 第二个参数:线程的属性是否继承
         PTHREAD_INHERIT_SCHED:表示线程继承调用者的属性。
         PTHREAD_EXPLICIT_SCHED:自己设定线程的属性值,为默认值。
       */
      pthread_attr_setinheritsched(&pthreadAttr, PTHREAD_EXPLICIT_SCHED);
      
    • scope:表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。

      /* 设置线程的优先级的有效范围
       *
       * 第一个参数:pthread_attr_t:线程属性对象,传地址
       * 第二个参数:线程的优先级的有效范围
         PTHREAD_SCOPE_SYSTEM:表示与系统中所有线程一起竞争CPU时间。
         PTHREAD_SCOPE_PROCESS:表示仅与同进程中的线程竞争CPU。
           */
      pthread_attr_setscope(&pthreadAttr, PTHREAD_SCOPE_SYSTEM)。
      
  • 创建线程:pthread_create
    1.1. 创建运行函数。
    void* pthreadRun(void* str) {
        printf("pthreadRun\n");
        NSLog(@"%@", [NSThread currentThread]);
        return (void*)110;
    }
    
    1.2. 创建线程。
    /*参数说明
     *
     * 第一个参数:pthread_t:线程对象,传地址
     * 第二个参数:pthread_attr_t:线程的属性,一般可以传入Null,则表明是非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。
     * 第三个参数:线程运行函数的起始地址。
     * 第四个参数:传给运行函数的参数。
     */
    pthread_create(&pthread, &pthreadAttr, pthreadRun, @"test1");
    

pthread_join()

  • 作用
    阻塞当前线程,等待某个线程结束,并获取这个线程的返回值,然后回收这个线程的资源。
  • 注意
    被等待的线程的分离状态必须是非分离状态(PTHREAD_CREATE_JOINABLE)。
    /*参数说明
     *
     * 第一个参数:pthread_t:线程对象
     * 第二个参数:用来存储被等待线程pthread_t的返回值
     */
    void* pthreadReturn = NULL;
    pthread_join(pthread, &pthreadReturn);
    NSLog(@"%d", (int)pthreadReturn);
    

pthread_detach()

  • 作用
    不会阻塞当前线程,pthread_t的线程结束后,并自动回收资源。
    可以在这个线程pthread_t的函数中调用pthread_detach(pthread_self())来自动回收资源。
  • 注意
    被等待的线程的分离状态必须是非分离状态(PTHREAD_CREATE_JOINABLE)。
    /*参数说明
     *
     * 第一个参数:pthread_t:线程对象
     */
    pthread_detach(pthread);
    NSLog(@"---------");
    

pthread_cancel()

  • 作用
    发送终止信号给pthread线程,停止线程的运行。
  • 注意
    1.1 线程的取消并不是实时的,需要一个取消点,而这个取消点一般是一个系统调用函数。
    如果线程中没有这个取消点,可以调用pthread_testcancel()函数,给当前线程自行设置一个取消点。
    1.2 pthread线程cancelstate属性要设置为:PTHREAD_CANCLE_ENABLE,也是默认值。
    PTHREAD_CANCLE_ENABLE:表示可以接受处理取消信号,设置线程状态为CANCLE。并终止任务执行。
    PTHREAD_CANCLE_DISABLE:表示可以忽略pthread_cancel()信号,并继续执行。
    1.3 pthread线程设置canceltype属性时,只有在cancelstate属性设置为PTHREAD_CANCLE_ENABLE的时候,才有效。
    PTHREAD_CANCLE_DEFFERED:表示收到信号后继续运行至下一个取消点再退出。推荐做法。(因为在终止线程之前必须要处理好内存回收防止内存泄漏,而手动设置取消点这种方式就可以让我们很自由的处理内存回收时机。)
    PTHREAD_CANCLE_DEFFERED:立即执行取消动作(退出),不推荐这样操作,可能造成内存泄漏等问题。
  • 返回值
    0:调用成功。

pthread_kill()

  • 作用
    该函数可以用于向指定的线程发送信号。测试线程是否存在/终止的方法。
    如果线程内不对信号进行处理,则调用默认的处理程式,如SIGQUIT信号会退出终止线程,SIGKILL会杀死线程等等,可以调用signal(SIGQUIT, sig_process_routine); 来自定义信号的处理程序。
  • 返回值
    0:调用成功。
    ESRCH:线程不存在。
    EINVAL:信号不合法。
    /*参数说明
     *
     * 第一个参数:pthread_t:线程对象
     * 第二个参数表示传递的signal参数,一般都是大于0的,这时系统默认或者自定义的都是有相应的处理程序。常用信号量宏可以在这里查看,#import <signal.h> .signal为0时,是一个被保留的信号,一般用这个保留的信号测试线程是否存在。
     */
    int kullType = pthread_kill(pthread, 0);
    NSLog(@"%d", kullType);
    

API总结

操作函数

pthread_create():创建一个线程
pthread_exit():终止当前线程
pthread_cancel():中断另外一个线程的运行
pthread_join():阻塞当前的线程,直到另外一个线程运行结束
pthread_attr_init():初始化线程的属性
pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
pthread_attr_getdetachstate():获取脱离状态的属性
pthread_attr_destroy():删除线程的属性
pthread_kill():向线程发送一个信号
pthread_equal(): 对两个线程的线程标识号进行比较
pthread_detach(): 分离线程
pthread_self(): 查询线程自身线程标识号

同步函数

数据类型:
  • pthread_mutex_t 互斥量,用于互斥访问,用pthread_mutex_init方法进行初始化
  • pthread_cond_t 条件变量,调用pthread_cond_init初始化
  • 互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步
方法:
  • pthread_mutex_t 互斥量
    pthread_mutex_init() 初始化互斥锁
    pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即当互斥锁空闲时,将占有该锁;否则,立即返回一个错误值EBUSY。常用于死锁检测,根据返回的错误信息程序员对死锁做出相应的处理
    pthread_mutex_lock():占有互斥锁(阻塞操作),函数调用会阻塞直到互斥量被unlock。
    pthread_mutex_unlock(): 释放互斥锁
    pthread_mutex_destroy() 删除互斥锁

  • pthread_mutex_t 条件变量:
    pthread_cond_init():初始化条件变量
    pthread_cond_wait(): 等待条件变量的特殊条件发生
    pthread_cond_signal(): 唤醒第一个调用pthread_cond_wait()而进入睡眠的线程
    pthread_cond_destroy():销毁条件变量
    pthread_key_create(): 分配用于标识进程中线程特定数据的键
    pthread_setspecific(): 为指定线程特定数据键设置线程特定绑定
    pthread_getspecific(): 获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中
    pthread_key_delete(): 销毁现有线程特定数据键
    pthread_attr_getschedparam();获取线程优先级
    pthread_attr_setschedparam();设置线程优先级

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

推荐阅读更多精彩内容