【APUE】线程-pthread

17 篇文章 6 订阅

【线程标识】

每个进程都有一个进程ID,每个线程也有一个线程ID。进程ID在整个系统中都是唯一的,但是线程ID不同,线程ID只有在它所属的进程的上下文中才有意义。

#include <pthread.h>
/*不同系统对于pthread_t结构定义不同,因此不能直接比较*/
int pthread_equal(pthread_t tid1, pthread_t tid2);

/*可以获取当前线程的ID*/
pthread_t pthread_self(void);

【线程创建】

#include <pthread.h>
/*
*return :0 success
*return :errno fail
*/
int pthread_create(pthread_t *restrict tidp, 
                    const pthread_atrr *restrict attr,
                    void *(start_rtn)(void *), 
                    void *restrict arg);

【线程终止】

如果进程中的任意线程调用了exit、_Exit、_exit,那么整个进程就会终止。

单个线程可以通过以下三种方式退出,因此可以在不终止整个进程的情况下,停止他的控制流

1.线程可以简单的从启动例程中返回,返回值是线程的退出码。

2.线程可以被统一进程的其他线程取消

3.线程调用pthread_exit

#include <pthread.h>
/*rval_ptr是一个无类型指针,与传给启动例程的单个参数类似,进程中的其他线程可以通过调用pthread_join函数访问到这个指针。*/
void pthread_exit(void *rval_ptr);

int pthread_join(pthread_t thread, void **rval_ptr);

调用线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程返回或者取消。如果线程简单从启动例程返回,rval_ptr就包含返回码。如果线程被取消,rval_ptr指定的内存单元就被设置为PTHREAD_CANCELED。

可以通过调用pthread_join自动将线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,则调用该函数会失败,返回EINVAL。

也可以把rval_ptr设置为NULL,这样调用pthread_join可以等待指定的线程终止,但不获取终止状态。

#include <pthread.h>
/*线程可以通过调用pthread_cancel来请求取消统一进程的其他线程*/
int pthread_cancel(pthread_t tid);

【线程清理程序】

线程退出时可以调用设置的函数,这与进程使用atexit函数安排退出是一样的。这样的函数被称为线程清理程序(thread cleanup handler)。一个线程可以建立多个清理程序。处理程序记录在栈中,即执行顺序和注册顺序相反。

#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);

当线程执行以下动作时,rtn是由pthread_cleanup_push调度的,调用时只有一个参数:

1.调用pthread_exit时

2.响应取消请求时

3.用非零参数调用pthread_cleanup_pop时

如果execute参数设置为0,清理函数不被调用。不管发生哪种情况,pthread_clean_pop都将删除上次pthread_cleanup_push调用建立的清理处理程序。

这些函数有一个限制,由于他们可以实现为宏,所以必须在与线程相同的作用域中以匹配对的形式使用。pthread_cleanup_push的宏定义可以包含字符,此时,pthread_cleanup_pop中要有对应的匹配字符。

【P V P】

进程原语线程原语描述
forkpthread_create创建新的控制流
exitpthread_exit从现有控制流退出
waitpidpthread_join从控制流中获取退出状态
atexitpthread_cleanup_push注册在退出控制流时调用的函数
getpidpthread_self获取控制流ID
abortpthread_cancel请求控制流的非正常退出

在默认情况下,线程的终止状态会保存到直到对这个线程调用pthread_join。但是如果线程已经被分离,则底层存储资源可以再线程终止时立即被收回。在线程被分离后,不能采用pthread_join获取终止状态,否则会出现未定义行为。

可以使用pthread_detach分离线程。

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

【线程同步-互斥量】

互斥变量用pthread_mutex_t数据结构表示,使用前,必须对它进行初始化,可以设置为PTHREAD_MUTEX_INITIALIZER(静态分配的互斥量),或者调用pthread_mutex_init进行初始化。如果动态分配互斥量,释放内存前需要调用pthread_mutex_destroy.

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

int pthread_mutex_destory(pthred_mutex_t *mutex);

对互斥量进行加锁,需要调用pthread_mutex_lock。如果已经加锁,调用线程将阻塞到互斥量被解锁。对互斥量解锁,需要调用pthread_mutex_unlock。

如果线程不希望被阻塞,它可以使用pthread_mutex_trylock对互斥量尝试加锁,如果调用pthread_mutex_trylock时互斥量处于未锁状态,那么将锁住互斥量,不会出现阻塞直接返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,返回EBUSY。

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

利用pthread_mutex_timedlock来避免永久阻塞。

#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                            const struct timespec *restrict tsptr);

【线程同步-读写锁】

读写锁三种状态:读模式加锁,写模式加锁,不加锁,提高并发性。

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                        const pthread_rwlockattr_t *restrict attr);/**default:NULL/
int pthread_rwlock_destory(pthread_rwlock_t *restrict rwlock);

/*lock unlock*/
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

/*带有超时的读写锁*/
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock
                                const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
                                const struct timespec *restrict tsptr);



【线程同步-自旋锁】

自旋锁和互斥量类似,但是它不是通过休眠来使进程阻塞,而是在获取锁之前一直处于忙等的阻塞状态(自旋)。

自旋锁用于以下情况:

锁被持有的时间短,而且线程不希望在重新调度上花费太多成本。

自旋锁通常作为底层原语用于实现其他类型的锁。

自旋锁在阻塞到可用这段时间,CPU是不能做其他事情的,因此自旋锁只能持有一小段时间。

自旋锁在非抢占式内核中非常有用:提供互斥机制;阻塞中断,这样中断处理程序就不会让系统陷入死锁,因为他需要获取已经被加锁的自旋锁。在分时调度用户层中,当时间片到期,或者具有更高优先级的线程就绪变为可运行时,用户线程就会被取消。如果线程拥有自旋锁,他就会进入休眠状态,阻塞在锁上的其他线程自旋的时间就会加长。

# include <pthread.h>
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destory(pthread_spinlock_t *lock);

int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

【线程同步-屏障barrier】

屏障是用户协调多个线程并行工作的同步机制,屏障允许每个进程等待,知道所有的合作线程都到达某一点,然后从该点继续执行。pthread_join函数就是一种屏障:允许一个线程等待,直到另一个线程退出。

#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
                        const pthread_barrierattr_t *restrict attr,
                            unsigned int count);
int pthread_barrier_destory(pthread_barrier_t *restrict barrier);

/*线程已经完成工作,准备等其他所有线程赶上来*/
int pthread_barrier_wait(pthread_barrier_t *barrier);

调用了pthread_barrier_wait的线程在屏障计数未满足条件时,会进入休眠状态,如果该线程是最后一个调用该函数的,就满足了计数值,所有线程就会被唤醒。

一旦到达屏障计数值,而且线程处于非阻塞状态,屏障就可以被重用。 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值