pthread库常用接口整理

一、线程操作

1、创建线程

/**
 * 参数
 * tidp: 指向线程标识符的指针
 * attr: 线程属性
 * start_rtn: 回调函数
 * arg: 运行回调函数所需参数
 *
 * 返回值
 * 创建成功返回 0, 否则返回错误码
 */
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void *)(*start_rtn)(void *), void *arg);

2、终止当前线程

// retval 用于获取线程退出时的返回值, 不需要时传入 NULL
void pthread_exit(void *retval);

3、获取当前线程 ID

pthread_t pthread_self();	// 常在线程函数内部调用

4、子线程合入父线程

/**
 * 参数
 * thread: 线程标识符
 * retval: 被等待线程结束时的返回值
 *
 * 返回
 * 成功返回 0, 否则返回错误码
 */
int pthread_join(pthread_t tid, void **retval);

// 描述:以阻塞的方式等待 thread 指定的线程结束,当函数返回时被等待线程资源被回收。如果线程已经结束,那么函数立即返回。
// 说明:被等待线程必须是 joinable 状态,pthread_create 后线程默认为 joinable 状态
// 作用:避免出现父线程先于子线程结束,导致子线程还没结束而主程序退出的情况

5、分离态线程

int pthread_detach(pthread_t tid); 

// 特点:使用 pthread_detach 后线程处于 detached 状态,线程退出后自己清理资源,线程不会阻塞主线程
// 使用:配合 sleep 或 while 使用,否则无法保证子线程先于主线程退出

6、线程私有数据

  • int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));

参数:key指向一个键指针;destructor为内存清理函数指针,如果这个参数不为NULL,那么当每个线程结束时,系统将调用这个函数来释放绑定在key上的内存块。

  • int pthread_key_delete(pthread_key_t key);

作用:注销一个TSD,这个函数并不检查当前是否有线程正在使用该TSD,也不会调用清理函数destructor,而只是将TSD释放以供下次调用pthread_key_create时使用。

  • int pthread_setspecific(pthread_key_t key, const void *pointer));void* pthread_getspecific(pthread_key_t key);

说明:pthread_setspecific是把一个变量的地址告诉key,一般放在变量定义之后,pthread_getspecific会把这个地址读出来,需转义成需要的类型再去操作(注意变量的有效期)。只不过,在不同线程里可以操作同一key,他们不会冲突,比如线程ABset 同样的key,分别 get 得到的地址会是之前各自传进去的值。这样做的意义在于,可以写一份线程代码,通过key的方式多线程操作不同的数据。
注意:pointer必须是动态分配空间,否则其他函数使用pthread_getspecific时会报错。

7、线程属性操作

// 线程属性
typedef struct
{
    int detachstate;			// 线程分离状态
    int schedpolicy;			// 线程调度策略
    struct sched_param schedparam;	// 线程调度参数
    int inheritsched;			// 线程继承性
    int scope;				// 线程作用域
    size_t guardsize;			// 线程栈末尾警戒缓冲区大小
    int stackaddr_set;			// 线程栈设置
    void *stackaddr;			// 线程栈位置
    size_t stacksize;			// 线程栈大小
} pthread_attr_t;

// 初始化线程属性对象, 成功返回 0, 否则失败
int pthread_attr_init(pthread_attr_t *attr);

// 销毁一个线程属性对象, 成功返回 0, 否则失败
int pthread_attr_destroy(pthread_attr_t *attr);

// 设置线程栈大小(创建线程时若不手动设置线程栈大小则由系统默认分配)
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

// 获取线程栈大小
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);

二、线程同步

1、互斥锁

  • 锁变量:pthread_mutex_t

  • 锁初始化:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);其中attr为锁属性

  • 锁释放:int pthread_mutex_destroy(pthread_mutex_t *mutex);被释放锁不可为上锁状态

  • 加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);

  • 解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);

2、条件变量

  • 条件变量:pthread_cond_t

  • 初始化条件变量:int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *caddr);初始化一个条件变量。当参数cattr为空指针时,函数创建的是一个缺省的条件变量。否则条件变量的属性将由cattr中的属性值来决定。调用pthread_cond_init函数时,参数cattr为空指针等价于cattr中的属性为缺省属性,只是前者不需要cattr所占用的内存开销。这个函数返回时,条件变量被存放在参数cond指向的内存中。可以用宏PTHREAD_COND_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_cond_init函数动态分配的效果是一样的。初始化时不进行错误检查。例如pthread_cond_t cond = PTHREAD_COND_INITIALIZER;不能由多个线程同时初始化一个条件变量。当需要重新初始化或释放一个条件变量时,应用程序必须保证这个条件变量未被使用。

  • 释放条件变量:int pthread_cond_destroy(pthread_cond_t *cond);

  • 阻塞:int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cond参数指向的条件变量上。被阻塞的线程可以被pthread_cond_signal函数或pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错后返回。一般一个条件表达式都是在一个互斥锁的保护下被检查。当条件表达式未被满足时,线程将仍然阻塞在这个条件变量上。当另一个线程改变了条件的值并向条件变量发出信号时,等待在这个条件变量上的一个线程或所有线程被唤醒,接着都试图再次占有相应的互斥锁。阻塞在条件变量上的线程被唤醒以后,直到pthread_cond_wait函数返回之前条件的值都有可能发生变化,所以函数返回以后,在锁定相应的互斥锁之前,必须重新测试条件值。最好的测试方法是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件,示例伪码见本段结束处。阻塞在同一个条件变量上的不同线程被释放的次序是不一定的。注意pthread_cond_wait函数是退出点,如果在调用这个函数时,已有一个挂起的退出请求,且线程允许退出,这个线程将被终止并开始执行其他逻辑代码,而这时和条件变量相关的互斥锁仍将处在锁定状态。

pthread_mutex_lock();
while(condition_is_false)
{
    pthread_cond_wait();
}
pthread_mutex_unlock();
  • 解除阻塞在指定条件变量上的一个线程:int pthread_cond_signal(pthread_cond_t *cond);必须在互斥锁的保护下使用相应的条件变量,否则对条件变量的解锁有可能发生在锁定条件变量之前,从而造成死锁。唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal将没有作用。

  • 解除阻塞在指定条件变量上的所有线程:int pthread_cond_broadcast(pthread_cond_t *cond);当没有线程阻塞在cond上时,使用pthread_cond_broadcast无效。注意,通过pthread_cond_broadcast唤醒的所有线程将竞争相应的互斥锁。

唤醒丢失问题

在线程未获得相应的互斥锁时调用pthread_cond_signalpthread_cond_broadcast可能会引起唤醒丢失问题。唤醒丢失往往会在下面的情况下发生:1、一个线程调用pthread_cond_signalpthread_cond_broadcast;2、另一个线程正处在测试条件变量和调用pthread_cond_wait之间;3、没有线程正处在阻塞等待的状态下。

posted @ 2022-08-12 16:04  HOracle  阅读(592)  评论(0编辑  收藏  举报