# 线程管理

# 基本操作

# pthread_create

创建一个线程

#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void *),
                   void *restrict arg);

Parameter:

  • thread --- 返回一个唯一的线程 ID
  • attr --- 可以指定线程属性对象,或者以 NULL 为缺省值
  • start_routine --- 线程执行函数
  • arg --- 为 start_routine 传递的参数,NULL 为缺省值

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EAGAIN --- 资源不足或超出创建线程数,无法创建
  • EINVAL --- 无效 attr 参数属性
  • EPERM --- 没有权限设置调度策略和 attr 中的参数

note:当 attr 属性为缺省 NULL 时,此线程默认为 joinable 属性。

# pthread_self

获取调用线程的 ID;可以查看一个线程自己的线程 ID,需要知道的是线程也有 ID 的,且具有唯一的线程 ID

#include <pthread.h>
pthread_t pthread_self(void);

Return:

  • 此函数始终成功,返回调用线程的 ID

# pthread_equal

比较俩个线程 ID,当线程 ID 相同时返回非 0,不同时返回 0

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);

Parameter:

  • t1 and t2:线程的 ID

Return:

  • nonzero --- 俩线程 ID 相等
  • zero --- 俩线程 ID 不同

# pthread_join

线程可以是 joinable 或 detached 的。 如果一个线程是 joinable,那么另一个线程可以调用 pthread_join() 等待该线程终止(挂起当前线程,阻塞式等待线程结束,如果线程已结束则立即返回)

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

Parameter:

  • thread --- 需要终止的线程 ID
  • retval --- 退出的状态返回值,用于提供给 pthread_exit()

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EDEADLK --- 检测到死锁或自身调用
  • EINVAL --- 该线程非 joinable 线程或另一个线程已经在等待加入这个线程
  • ESRCH --- 传入的 thread 参数是不存在的线程 ID

# pthread_detach

用于把 joinable 线程转变为 detached 线程,一旦把 joinable 线程被设置为 detached 的,那么这个线程就不能被 pthread_join() 调用

#include <pthread.h>
int pthread_detach(pthread_t thread);

Parameter:

  • thread --- 需要修改的线程 ID

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EINVAL --- 该线程非 joinable 线程
  • ESRCH --- 传入的 thread 参数是不存在的线程 ID

# pthread_exit

终止线程,也就是终止自己的执行

#include <pthread.h>
void pthread_exit(void *retval);

Parameter:

  • retval --- 由 pthread_join() 提供参数,或以 NULL 为缺省值自身调用结束

# 参数配置

# pthread_attr_t

pthread_attr_t 数据类型,以下函数都调用该结构属性:

typedef struct
{
       int                       detachstate;   // 线程的分离状态
       int                       schedpolicy;   // 线程的调度策略
       structsched_param         schedparam;    // 线程的优先级
       int                       inheritsched;  // 线程的继承性
       int                       scope;         // 线程的作用域
       size_t                    guardsize;     // 线程栈的警戒缓冲区大小
       int                       stackaddr_set; // 线程的栈设置
       void*                     stackaddr;     // 线程栈的地址
       size_t                    stacksize;     // 线程栈的大小
} pthread_attr_t;
  • detachstate:表示新线程是否与进程中其他线程脱离同步, 如果设置为 PTHREAD_CREATE_DETACHED 则新线程不能用 pthread_join() 来同步,且在退出时自行释放所占用的资源;缺省为 PTHREAD_CREATE_JOINABLE 状态。

    这个属性也可以在线程创建并运行以后用 pthread_detach() 来设置,而一旦设置为 PTHREAD_CREATE_DETACH 状态(不论是创建时设置还是运行时设置)则不能再恢复到 PTHREAD_CREATE_JOINABLE 状态。

  • schedpolicy:表示新线程的调度策略,主要包括 SCHED_OTHER (正常、非实时)、 SCHED_RR (实时、轮转法)和 SCHED_FIFO (实时、先入先出)三种,缺省为 SCHED_OTHER ,后两种调度策略仅对超级用户有效;运行时可以用过 pthread_setschedparam() 来改变。

  • schedparam:一个 struct sched_param 结构,目前仅有一个 sched_priority 整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即 SCHED_RRSCHED_FIFO )时才有效,并可以在运行时通过 pthread_setschedparam() 函数来改变,缺省为 0。

  • inheritsched:有两种值可供选择: PTHREAD_EXPLICIT_SCHEDPTHREAD_INHERIT_SCHED ,前者表示新线程使用显式指定调度策略和调度参数(即由 schedpolicyschedparam 属性确定),而后者表示继承调用者线程的调度策略和调度参数。缺省为 PTHREAD_EXPLICIT_SCHED

  • scope:表示线程间竞争 CPU 的范围,也就是说线程优先级的有效范围。POSIX 的标准中定义了两个值: PTHREAD_SCOPE_SYSTEMPTHREAD_SCOPE_PROCESS ,前者表示与系统中所有线程一起竞争资源(CPU 等),后者表示仅与同一进程中的线程竞争资源(CPU 等)。目前 LinuxThreads 仅实现了 PTHREAD_SCOPE_SYSTEM 值。

  • guardsize:控制着线程栈保护区大小,以避免栈溢出,默认设置为系统的页大小。

    可以把 guardsize 线程属性设为 0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓存区。同样地,如果对线程属性 stackaddr 作了修改,系统就会假设我们会自己管理栈,并使警戒栈缓冲区机制无效,等同于把 guardsize 线程属性设为 0。

    如果 guardsize 大于零,则会为每个使用 attr 创建的线程提供大小至少为 guardsize 字节的溢出保护区。

  • stackaddr_set:

  • stackaddr:该属性设置新线程所用栈的栈地址。

  • stacksize:该属性设置新线程所用栈的栈大小。

# pthread_attr_init

线程属性初始化

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);

# pthread_attr_destroy

销毁线程属性对象

#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);

# 设置 / 获取线程的分离状态

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
// detachstate:PTHREAD_CREATE_DETACHED or PTHREAD_CREATE_JOINABLE
int pthread_attr_setdetachstate(pthread_attr_t *attr,
                                int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr,
                                int *detachstate);

# 设置 / 获取线程的调度策略

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
// policy:SCHED_FIFO, SCHED_RR or SCHED_OTHER
int pthread_attr_setschedpolicy(pthread_attr_t *attr,
                                int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr,
                                int *restrict policy);

# 设置 / 获取线程的优先级

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
int pthread_attr_setschedparam(pthread_attr_t *restrict attr,
                               const struct sched_param *restrict param);
int pthread_attr_getschedparam(const pthread_attr_t *restrict attr,
                               struct sched_param *restrict param);

所用系统支持的优先级最大最小值可以使用如下函数查询:

#include <sched.h>
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);

note:

该属性值仅当调度策略为实时(即 SCHED_RR 或 SCHED_FIFO )时才有效,因此一般使用以下函数设置优先级:

#include <pthread.h>
struct sched_param sched;
sched.sched_priority = -1;
pthread_t pidtake;
pthread_create(&pidtake, NULL, thread_take, NULL);
pthread_setschedparam(pidtake, SCHED_RR, &sched);

# 设置 / 获取线程的继承性

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
// inheritsched:PTHREAD_INHERIT_SCHED or PTHREAD_EXPLICIT_SCHED
int pthread_attr_setinheritsched(pthread_attr_t *attr,
                                 int inheritsched);
int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr,
                                 int *restrict inheritsched);

# 设置 / 获取线程的作用域

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
// scope:PTHREAD_SCOPE_SYSTEM or PTHREAD_SCOPE_PROCESS
int pthread_attr_setscope(pthread_attr_t *attr,
                          int scope);
int pthread_attr_getscope(const pthread_attr_t *restrict attr,
                          int *restrict scope);

# 设置 / 获取线程栈保护区大小

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
int pthread_attr_setguardsize(pthread_attr_t *attr,
                              size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,
                              size_t *restrict guardsize);

# 设置 / 获取线程的内存区域

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
int pthread_attr_setstackaddr(pthread_attr_t *attr,
                              void *stackaddr);
int pthread_attr_getstackaddr(const pthread_attr_t *restrict attr,
                              void **restrict stackaddr);

# 设置 / 获取线程的栈大小

该属性值设置及获取由如下两个函数进行:

#include <pthread.h>
int pthread_attr_setstacksize(pthread_attr_t *attr,
                              size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,
                              size_t *restrict stacksize);

# 信号量

# sem_init

初始化一个匿名信号量

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

Parameter:

  • sem --- 返回一个信号量
  • pshared --- 为 0 表示该信号量在某个进程中的多个线程之间共享,该信号量应当能够被所有的线程访问,例如是一个全局变量或者是在堆上动态分配的变量;若不为 0 ,表示在进程间共享,那么该信号量应该位于共享内存的区域中
  • value --- 指定信号量的初始值

Return:

  • zero --- success
  • nonzero(-1) --- error

# sem_wait / sem_trywait / sem_timedwait

锁定给定的信号量

对于 sem_wait ,如果信号量的值 sval 大于 0 , sem_wait 会对 sval 减 1 ,函数立即返回。如果 sval 为 0 , sem_wait 将阻塞调用者(某个进程或线程),直到 sval 重新大于 0 或者调用者被 Linux 系统中的系统调用 signal() 函数中断 。

对于 sem_trywait ,如果对 sval 的减 1 操作不能完成, sem_trywait 不会阻塞调用者,而是返回一个 error ,并设置 errnoEAGAIN

对于 sem_timedwait ,如果 sval 为 0,则阻塞等待,当阻塞时长超过 abs_timeout 返回失败( errno 设置为 ETIMEDOUT ) 。

#include <semaphore.h>
struct timespec {
    time_t tv_sec;      /* Seconds */
    long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
};
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *restrict sem,
                  const struct timespec *restrict abs_timeout);

Parameter:

  • sem --- 需加锁的信号量
  • abs_timeout --- struct timespec 结构的超时时间值

Return:

  • zero --- success
  • nonzero(-1) --- error

# sem_post

释放给定的信号量,对信号量的值 sem_val 进行加 1 操作,如果该信号量的值最终大于 0 ,则唤醒被 semwait() 阻塞的另一个进程或线程,并重新锁定该信号量

#include <semaphore.h>
int sem_post(sem_t *sem);

Parameter:

  • sem --- 需解锁的信号量

Return:

  • zero --- success
  • nonzero(-1) --- error

# sem_getvalue

获取给定信号量的值

#include <semaphore.h>
int sem_getvalue(sem_t *restrict sem, int *restrict sval);

Parameter:

  • sem --- 需获取的信号量
  • sval --- 返回的当前信号量的值

Return:

  • zero --- success
  • nonzero(-1) --- error

# sem_destroy

销毁一个匿名信号量

#include <semaphore.h>
int sem_destroy(sem_t *sem);

Parameter:

  • sem --- 需销毁的信号量

Return:

  • zero --- success
  • nonzero(-1) --- error

# 互斥锁

在多线程编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。 每个对象都对应于一个可称为 "互斥锁" 的标记,这个标记用来保证在任一时刻, 只能有一个线程访问该对象。互斥锁也可以叫线程锁

# 基本操作

# PTHREAD_MUTEX_INITIALIZER

静态初始化互斥锁

# define PTHREAD_MUTEX_INITIALIZER \
   { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

eg:

pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;

# pthread_mutex_init

动态初始化互斥锁

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
                       const pthread_mutexattr_t *mutexattr);

Parameter:

  • mutex --- 返回一个唯一的互斥锁 ID
  • mutexattr --- 可以指定互斥锁属性对象,或者以 NULL 为缺省值

Return:

  • zero --- 此函数始终成功

# pthread_mutex_lock

锁定给定的互斥锁,阻塞调用。如果这个互斥锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个互斥锁的等待队列中,并会进入阻塞状态, 直到拿到该锁之后才会返回。

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

Parameter:

  • mutex --- 需加锁的互斥锁 ID

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EINVAL --- 未正确初始化
  • EDEADLK --- 互斥锁已经被调用线程锁定(仅 PTHREAD_MUTEX_ERRORCHECK 类型互斥锁)

# pthread_mutex_trylock

锁定给定的互斥锁,阻塞调用。当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY ,意思是说, 有其它线程正在使用这个锁。

#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);

Parameter:

  • mutex --- 需加锁的互斥锁 ID

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EBUSY --- 互斥锁无法获取,因为目前已被锁定占用

  • EINVAL --- 未正确初始化

# pthread_mutex_unlock

释放给定的互斥锁

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Parameter:

  • mutex --- 需解锁的互斥锁 ID

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EINVAL --- 未正确初始化
  • EPERM --- 调用线程未拥有互斥锁(仅 PTHREAD_MUTEX_ERRORCHECK 类型互斥锁)

# pthread_mutex_destroy

销毁由 pthread_mutex_init 动态初始化的互斥锁,释放它可能持有的资源

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);

Parameter:

  • mutex --- 需销毁的互斥锁 ID

Return:

  • zero --- success
  • nonzero --- error code on Errors

Errors:

  • EBUSY --- 互斥锁目前已被锁定占用,无法进行销毁

# 参数配置

# pthread_mutexattr_t

pthread_mutexattr_t 数据类型,以下函数都调用该结构属性:

/* Data structures for mutex handling.  The structure of the attribute
   type is not exposed on purpose.  */
typedef union
{
    struct __pthread_mutex_s
    {
        int __lock;
        unsigned int __count;
        int __owner;
#if __WORDSIZE == 64
        unsigned int __nusers;
#endif
        /* KIND must stay at this position in the structure to maintain
           binary compatibility.  */
        int __kind;
#if __WORDSIZE == 64
        int __spins;
        __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV  1
#else
        unsigned int __nusers;
        __extension__ union
        {
            int __spins;
            __pthread_slist_t __list;
        };
#endif
    } __data;
    char __size[__SIZEOF_PTHREAD_MUTEX_T];
    long int __align;
} pthread_mutex_t;

# pthread_mutexattr_init

互斥锁属性初始化

#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);

# pthread_mutexattr_destroy

销毁互斥锁属性对象

#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

# pthread_mutexattr_settype

设置互斥锁类型属性

#include <pthread.h>
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);

如果互斥锁类型为 PTHREAD_MUTEX_NORMAL ,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产生不确定的行为。

如果互斥锁类型为 PTHREAD_MUTEX_ERRORCHECK ,则会提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。

如果互斥锁类型为 PTHREAD_MUTEX_RECURSIVE ,则该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为 1。线程每重新锁定该互斥锁一次,锁定计数就增加 1。线程每解除锁定该互斥锁一次,锁定计数就减小 1。 锁定计数达到 0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。

如果互斥锁类型是 PTHREAD_MUTEX_DEFAULT ,则尝试以递归方式锁定该互斥锁将产生不确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为。

# 条件变量

# 读写锁

# 自旋锁

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

夏沫の浅雨 微信支付

微信支付

夏沫の浅雨 支付宝

支付宝