了解 POISX Thread

什么是 POSIX Threads

    POSIX Threads (通常被缩写为 Pthreads)是 POSIX (可移植操作系统接口,Portable Operating System Interface)的线程标准,定义了创建和操作线程的一套 API

Pthreads

    实现 POSIX 线程标准的库常被称作 PthreadsPthreads 定义了一套 C 语言的类型、函数与常量,它以 pthread.h 头文件和一个线程库实现。

Pthreads API 中大致共有 100个 函数调用,全都以 pthread_头,并可以分为以下四类:

  • 线程管理,例如创建线程,等待(join)线程,查询线程状态等。
  • 互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作
  • 条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作
  • 使用了互斥锁的线程间的同步管理

数据类型

pthread_t

    pthread_t 是线程句柄。出于可移植目的,不能把它作为整数处理,应使用函数 pthread_equal() 对两个线程 id 进行比较。获取自身所在线程 id 使用函数为 pthread_self()

pthread_attr_t

    pthread_attr_t 是线程属性。主要包括 scope属性、detach属性、堆栈地址、堆栈大小、优先级。主要属性的意义如下:

  • __detachstate,表示新线程是否与进程中其他线程脱离同步

    • 设置为PTHREAD_CREATE_DETACHED, 则新线程不能用pthread_join() 来同步,且在退出时自行释放所占用的资源。
    • 默认为 PTHREAD_CREATE_JOINABLE。可以在线程创建并运行以后用pthread_detach() 来设置
    • 设置为 PTHREAD_CREATE_DETACHED 状态,不论是创建时设置还是运行时设置,则不能再恢复到 PTHREAD_CREATE_JOINABLE 状态。
  • __schedpolicy,表示新线程的调度策略,默认值为 SCHED_OTHER,后两种调度策略仅对超级用户有效,运行时可以用过 pthread_setschedparam() 来改变

    • SCHED_OTHER,正常、非实时
    • SCHED_RR,实时、轮转法
    • SCHED_FIFO,实时、先入先出
  • __schedparam,一个 struct sched_param 结构,目前仅有一个 sched_priority 整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即 SCHED_RRSCHED_FIFO )时才有效,并可以在运行时通过 pthread_setschedparam() 函数来改变,默认为0。系统支持的最大和最小的优先级值可以用函数 sched_get_priority_maxsched_get_priority_min 得到。

  • __inheritsched ,默认为 PTHREAD_EXPLICIT_SCHED

    • PTHREAD_EXPLICIT_SCHED 表示新线程使用显式指定调度策略和调度参数(即attr中的值),
    • PTHREAD_INHERIT_SCHED而后者表示继承调用者线程的值。
  • __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围

    • PTHREAD_SCOPE_SYSTEM,表示与系统中所有线程一起竞争CPU时间,
    • PTHREAD_SCOPE_PROCESS 后者表示仅与同进程中的线程竞争CPU。

pthread_barrier_t

    同步屏障数据类型

pthread_mutex_t

    pthread_mutex_t 是线程互斥锁数据类型,有两种方法创建互斥锁:

  • 静态方式
    POSIX 定义了一个宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,

  • 动态方式
    采用 pthread_mutex_init() 函数来初始化互斥锁,其中 mutexattr 用于指定互斥锁属性,如果为 NULL 则使用默认属性:

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr) 
    

pthread_mutexattr_t

    互斥锁的属性 pthread_mutexattr_t 在创建锁的时候指定,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。在 iOS 中有以下几个类型可选:

  • PTHREAD_MUTEX_NORMAL
    这是默认值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  • PTHREAD_MUTEX_ERRORCHECK
    检错锁,如果同一个线程请求同一个锁,则返回EDEADLK。
  • PTHREAD_MUTEX_RECURSIVE
     嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

pthread_cond_t

     pthread_cond_t 是条件变量数据类型,条件变量和互斥锁一样,都有静态和动态两种创建方式,

  • 静态方式
    使用 PTHREAD_COND_INITIALIZER 常量进行初始化

  • 动态方式
    调用 pthread_cond_init() 函数

操作线程函数

创建一个线程

int pthread_create(pthread_t _Nullable * _Nonnull __restrict,
  const pthread_attr_t * _Nullable __restrict,
  void * _Nullable (* _Nonnull)(void * _Nullable),
  void * _Nullable __restrict);

终止当前线程

pthread_exit(void)

中断一个线程

int pthread_cancel(pthread_t)

阻塞当前的线程

阻塞当前的线程,直到另外一个线程运行结束

int pthread_join(pthread_t , void * _Nullable * _Nullable)
  __DARWIN_ALIAS_C(pthread_join);

向指定ID的线程发送一个信号

    向指定ID的线程发送一个信号,如果线程不处理该信号,则按照信号默认的行为作用于整个进程。信号值0为保留信号,作用是根据函数的返回值判断线程是不是还活着。

int pthread_kill(pthread_t threadId,int signal);

线程属性函数

初始化线程属性变量

int pthread_attr_init(pthread_attr_t *);

设置/获取线程属性变量的 detachstate 属性

int pthread_attr_setdetachstate(pthread_attr_t *, int);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);

设置/获取 scope

int pthread_attr_setscope(pthread_attr_t *, int);
int pthread_attr_getscope(const pthread_attr_t * __restrict, int * __restrict);

设置/获取 schedparam

int pthread_attr_setschedparam(pthread_attr_t * __restrict,
  const struct sched_param * __restrict);
int pthread_attr_getschedparam(const pthread_attr_t * __restrict,
  struct sched_param * __restrict);

销毁线程属性

int pthread_attr_destroy(pthread_attr_t *);

互斥锁函数

初始化互斥锁

  int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr) 

销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t *);

加锁

int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);

解锁

int pthread_mutex_unlock(pthread_mutex_t *);

条件变量函数

初始化条件变量

int pthread_cond_init(
  pthread_cond_t * __restrict,
  const pthread_condattr_t * _Nullable __restrict)
  __DARWIN_ALIAS(pthread_cond_init);

销毁条件变量

int pthread_cond_destroy(pthread_cond_t *);

等待条件变量的特殊条件发生

    pthread_cond_wait() 必须与一个 pthread_mutex 配套使用。该函数调用实际上依次做了3件事:

  1. 对当前 pthread_mutex 解锁
  2. 把当前线程挂起到当前条件变量的线程队列
  3. 被其它线程的信号唤醒后对当前 pthread_mutex 申请加锁。
int pthread_cond_wait(pthread_cond_t * __restrict,
  pthread_mutex_t * __restrict) __DARWIN_ALIAS_C(pthread_cond_wait);

发送一个信号

    pthread_cond_signal 发送一个信号给正在当前条件变量的线程队列中处于阻塞等待状态的线程,使其脱离阻塞状态,唤醒后继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal 也会成功返回。一般只给一个阻塞状态的线程发信号。假如有多个线程正在阻塞等待当前条件变量,则根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但 pthread_cond_signal 在多处理器上可能同时唤醒多个线程,当只能让一个被唤醒的线程处理某个任务时,其它被唤醒的线程就需要继续 wait

int pthread_cond_signal(pthread_cond_t *);

工具函数

查询线程自身线程标识号

pthread_t pthread_self(void);

比较两个线程标识

int pthread_equal(pthread_t _Nullable, pthread_t _Nullable);

执行一次

某些需要仅执行一次的函数。其中第一个参数为 pthread_once_t 类型,是内部实现的互斥锁,保证在程序全局仅执行一次。

int pthread_once(pthread_once_t *, void (* _Nonnull)(void));

线程私有存储(Thread-local storage,简写 tls)

Thread-local storage 是操作系统为线程单独提供的私有空间,只有有限的容量。通常通过 pthread 库中的函数实现:

创建 key

分配用于标识进程中线程特定数据的 pthread_key_t 类型的键

int pthread_key_create(pthread_key_t *, void (* _Nullable)(void *));

销毁现有线程特定数据 Key

int pthread_key_delete(pthread_key_t);

为指定线程的特定数据键设置绑定的值

extern int pthread_setspecific(unsigned long, const void*);

获取绑定的值

extern void *pthread_getspecific(unsigned long);

objc tls 实现

 typedef pthread_key_t tls_key_t;

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

推荐阅读更多精彩内容