pthread的互斥量和自旋锁

一、自旋锁与互斥量的区别

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


信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用而不能在中断上下文使用,因为中断的上下文不允许休眠(trylock可以),因此在中断上下文只能使用自旋锁。


自旋锁保持期间是抢占失效的(内核不允许被抢占) ,而信号量和读写信号量保持期间是可以被抢占的。


自旋锁保护的临界区默认是可以相应中断的,但是如果在中断处理程序中请求相同的自旋锁,那么会发生死锁(内核自旋锁可以关闭中断)。

二、互斥量

互斥量初始化:动态初始化和编译期初始化

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

动态初始化是通过调用pthread_mutex_init函数实现的,如果是动态初始化为了防止初始化期间竞争,通常使用下面的方式

static pthread_once_t foo_once = PTHREAD_ONCE_INIT;
static pthread_mutex_t foo_mutex;


void foo_init()
{
    pthread_mutex_init(&foo_mutex, NULL);
}


void foo()
{
    pthread_once(&foo_once, foo_init);
    pthread_mutex_lock(&foo_mutex);
   /* Do work. */
    pthread_mutex_unlock(&foo_mutex);
}


互斥量属性

int pthread_attr_init(pthread_attr_t* attr);//初始化线程属性对象
int pthread_attr_destroy(pthread_attr_t * attr);//销毁线程属性对象,被销毁的线程属性对象只有再次初始化之后才能使用
下面的函数用于获取和设置线程属性对象的某个属性
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_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差不多。

互斥量操作

对锁的操作主要包括加锁 pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个。
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待

注意:

1、线程在阻塞等待互斥量的时候可以响应信号,从信号处理函数返回之后会依然阻塞等待信号量,这点与阻塞系统调用不同(返回-1,errno=EINT)。

2、线程在获取互斥量之后可能被取消(pthread_cancel),因此必须配合使用pthread_cleanup_push / pthread_cleanup_pop系统调用释放以获取的互斥量。

三、自旋锁

自旋锁初始化
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 对象即可(pthread_mutex_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)
;

展开阅读全文

没有更多推荐了,返回首页