pthread api介绍

pthread api介绍
#线程相关

创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); 
/*
thread:线程句柄
attr: 线程属性,这个参数是可选的,当不需要修改线程的默认属性时,给它传递NULL就行
start_routine: 入口函数,函数返回值,可以通过pthread_join()接口获得
arg: 入口函数参数
创建线程成功,则返回0
*/

线程合并

int pthread_join(pthread_t thread,void **value_ptr);
/*
value_ptr:start_routine返回的指针
这个接口会阻塞调用进程或线程,直到被合并的线程结束为止。当被合并线程结束,pthread_join()接口就会回收这个线程的资源,并将这个线程的返回值返回给合并者。
*/

线程分离

int pthread_detach(pthread_t tid);
/*
线程分离是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。因为线程分离是启动系统的自动回收机制,那么程序也就无法获得被分离线程的返回值
*/

线程参数

int pthread_attr_init (pthread_attr_t * attr);
int pthread_attr_destroy (pthread_attr_t * attr);
参数的初始化与销毁,创建完线程记着销毁了,新线程里面不需要使用
int pthread_attr_getdetachstate (const pthread_attr_t * attr,int *detachstate);
int pthread_attr_setdetachstat(pthread_attr_t *attr, int detachstate); 
detachstate有两个取值:PTHREAD_CREATE_DETACHED(分离的)和PTHREAD_CREATE_JOINABLE(可合并的,也是默认属性)
int pthread_attr_setschedpolicy (pthread_attr_t *, int policy);
int pthread_attr_getschedpolicy (const pthread_attr_t *,int *policy);
SCHED_RR(轮询):轮询指的是时间片轮转,当线程的时间片用完,系统将重新分配时间片,并将它放置在就绪队列尾部,这样可以保证具有相同优先级的轮询任务获得公平的CPU占用时间
SCHED_FIFO(先进先出):先进先出就是先到先服务,一旦线程占用了CPU则一直运行,直到有更高优先级的线程出现或自己放弃
SCHED_OTHER(其它)
int pthread_attr_getstackaddr (const pthread_attr_t * attr,void **stackaddr);
int pthread_attr_getstacksize (const pthread_attr_t * attr,size_t * stacksize);
int pthread_attr_setstackaddr (pthread_attr_t * attr,void *stackaddr);
int pthread_attr_setstacksize (pthread_attr_t * attr,size_t stacksize);
Linux系统为每个线程默认分配了8MB的堆栈空间,如果觉得这个空间不够用,可以通过修改线程的堆栈大小属性进行扩容。堆栈大小以字节为单位。需要注意的是,线程堆栈不能小于16KB,而且尽量按4KB(32位系统)或2MB(64位系统)的整数倍分配,也就是内存页面大小的整数倍
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
既然线程是有堆栈的,而且还有大小限制,那么就一定会出现将堆栈用满的情况。线程的堆栈用满是非常危险的事情,因为这可能会导致对内核空间的破坏,一旦被有心人士所利用,后果也不堪设想。为了防治这类事情的发生,Linux为线程堆栈设置了一个满栈警戒区。这个区域一般就是一个页面,属于线程堆栈的一个扩展区域。一旦有代码访问了这个区域,就会发出SIGSEGV信号进行通知。同时,如果我们修改了线程堆栈的大小,那么系统会认为我们会自己管理堆栈,也会将警戒区取消掉,如果有需要就要开启它。

线程同步

互斥锁

linux posix互斥锁在没有并发冲突的时候,是很高效的(libpthread-glibc-2.23-hurd+libpthread-20161218中通过__pthread_spinlock_t __held;__pthread_spinlock_t __lock;2个变量实现),但是如果有并发冲突,那么没有获取到锁的线程会入队并且堵塞同时发生CPU切换

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);  
int pthread_mutex_destory(pthread_mutex_t *mutex );  
int pthread_mutex_lock(pthread_mutex_t *mutex);  
int pthread_mutex_trylock(pthread_mutex_t *mutex);  //如果加锁失败,返回EBUSY
int pthread_mutex_unlock(pthread_mutex_t *mutex); 
//参数
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
int pthread_mutexattr_getshared(const pthread_mutexattr_t* attr, int* pshared);
int pthread_mutexattr_setshared(pthread_mutexattr_t* attr, int* pshared);
PTHREAD_PROCESS_SHARED:互斥锁可以被跨进程共享,然后在进程共享内存中分配pthread_mutex_t
PTHREAD_PROCESS_PRIVATE:只能被初始化线程所属的进程中的线程共享
int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type);
int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
PTHREAD_MUTEX_NOMAL:
公平锁,对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。
PTHREAD_MUTEX_ERRORCHECK:检错锁,对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLOCK。对一个已经被其他线程加锁的检错锁解锁,或者对一个已经解锁的检错锁再次解锁,则解锁操作返回EPERM。
PTHREAD_MUTEX_RECURSIVE:嵌套锁,错误使用返回EPERM
PTHREAD_MUTEX_DEFAULT:跟nomal差不多。

条件变量

一个完整的等待通知机制:线程首先获取互斥锁,当线程要求的条件不满足时,释放互斥锁,进入等待状态;当要求的条件满足时,通知等待的线程,重新获取互斥锁。

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);  
int pthread_cond_destory(pthread_cond_t *cond);  
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);  
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex, const timespec *abstime);  
/*
调用wait函数后,会把线程压力等待队列后释放锁同时堵塞自己,故调用wait前需要获取锁。由于调用pthread_cond_signal与wait获取到锁退出后不是同时发生的,故wait退出后,条件可能已经不满足了,故一般需要在while循环里:
lock()
while(条件不满足)
{
	wait();
}
临界区
unlock();
*/
int pthread_cond_signal(pthread_cond_t *cond);  
int pthread_cond_broadcast(pthread_cond_t *cond);
/*
pthread_cond_signal() 是会随机地通知等待队列中的一个线程,而 pthread_cond_broadcast() 会通知等待队列中的所有线程。从感觉上来讲,应该是 pthread_cond_signal() 更好一些,因为即便通知所有线程,也只有一个线程能够进入临界区。但那所谓的感觉往往都蕴藏着风险,实际上使用 pthread_cond_signal() 也很有风险,它的风险在于可能导致某些线程永远不会被通知到。假设我们有资源 A、B、C、D,线程 1 申请到了 AB,线程 2 申请到了 CD,此时线程 3 申请 AB,会进入等待队列(AB 分配给线程 1,线程 3 要求的条件不满足),线程 4 申请 CD 也会进入等待队列。我们再假设之后线程 1 归还了资源 AB,如果使用 pthread_cond_signal() 来通知等待队列中的线程,有可能被通知的是线程 4,但线程 4 申请的是 CD,所以此时线程 4 还是会继续等待,而真正该唤醒的线程 3 就再也没有机会被唤醒了。
那什么时候可以使用 pthread_cond_signal()呢?需要满足以下三个条件:所有等待线程拥有相同的等待条件;所有等待线程被唤醒后,执行相同的操作;只需要唤醒一个线程。
*/

自旋锁

在多处理器环境中,自旋锁最多只能被一个可执行线程持有。如果一个可执行线程试图获得一个被争用(已经被持有的)自旋锁,那么该线程就会一直进行忙等待,自旋,也就是空转,等待锁重新可用。如果锁未被争用,请求锁的执行线程便立刻得到它,继续执行。一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋,特别的浪费CPU时间,所以自旋锁不应该被长时间的持有。实际上,这就是自旋锁的设计初衷,在短时间内进行轻量级加锁。

int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
//如果想要使用自旋锁同步多进程,那么设置pshared=PTHREAD_PROCESS_SHARED,然后在进程共享内存中分配pthread_spinlock_t。
//自旋锁操作
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

读写锁

读写锁都遵守以下三条基本原则:允许多个线程同时读共享变量;只允许一个线程写共享变量;如果一个写线程正在执行写操作,此时禁止读线程读共享变量。
读写锁与互斥锁的一个重要区别就是读写锁允许多个线程同时读共享变量,而互斥锁是不允许的,这是读写锁在读多写少场景下性能优于互斥锁的关键。但读写锁的写操作是互斥的,当一个线程在写共享变量的时候,是不允许其他线程执行写操作和读操作。

int pthread_rwlock_init(pthread_rwlock_t *lock,const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *lock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
int pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,const struct timespec *abstime);
int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,const struct timespec *abstime);
int pthread_rwlock_unlock(pthread_rwlock_t *lock);
int pthread_rwlockattr_init (pthread_rwlockattr_t * attr);
int pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);
int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,int *pshared);
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,int pshared);

barrier

/*
barrier的意思是当线程到达barrier时,它将在barrier处等待,直到所有线程到达barrier,然后它们将一起继续运行并且重置barrier的数量到初始值。
*/
int pthread_barrierattr_init (pthread_barrierattr_t * attr);
int pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
int pthread_barrierattr_getpshared (const pthread_barrierattr_t* attr, int *pshared);
int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, int pshared);

int pthread_barrier_init (pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, unsigned int count);
int pthread_barrier_destroy (pthread_barrier_t * barrier);
int pthread_barrier_wait (pthread_barrier_t * barrier);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值