精彩文章免费看

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)。
    
    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();设置线程优先级

  • 禁止转载,