Libvirt的job机制2--条件变量基本原理

上篇介绍了libvirt中的job机制是如何使用的,在介绍job机制的原理之前,需要介绍条件变量是如何使用的,因为在libvirt中使用的job机制,是基于条件变量的。

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”;另一个线程使得“条件成立”(即发出条件变量)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

条件变量创建

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

尽管POSIX标准中为信号量定义了属性,但是在LinuxThreads中未实现,因此cond_attr值通常为NULL,且被忽略。

条件变量注销

注销一个条件变量需要调用pthread_cond_destroy(),只有在没有thread等待该条件变量的时候才能注销这个条件变量,否则返回EBUSY。因为Linux在实现条件变量时并未分配什么资源,因此注销动作仅包含检查是否有等待线程。

注销函数定义如下:

int pthread_cond_destroy(pthread_cond_t *cond)

条件变量等待

条件变量的等待方法包括两种:

无条件等待pthread_cond_wait()和计时等待pthread_cond_timewait(),其中计时等待方式如果在既定时刻前未满足条件,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格里尼治时间(1970.1.1.0:0:0)

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait())的竞争条件(Race Condition)

mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本thread加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁(即将con放入队列之后,再解锁)。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

/************pthread_cond_wait()的使用方法**********/

    pthread_mutex_lock(&mutex)

    pthread_cond_wait(&cond &mutex);

    pthread_mutex_unlock(&cond);

/*****************************************************/

这里需要说明的是,在libvirt中qemuDomainObjBeginJobInternal函数中调用的是virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) ,其中传入的互斥量是obj->parent.lock,这个互斥量是在进入API的时候,就已经被获取了,相当于这里的pthread_mutex_lock。

激发条件

有两种形式,pthread_cond_signal()激活有个等待条件的线程,存在多个等待thread时按入队列顺序激活其中一个;pthread_cond_broadcast()则激活所有等待thread

pthread_mutex_lock(&mutex);
ready = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

在发送条件变量信号的时候,也得加锁。但是在libvirt中看不到这个加锁和解锁的过程,原因是加锁是在进入API时完成的,解锁是在退出API时完成的,而发出信号是在这期间完成的。

为什么要加互斥锁,其目的是什么

首先需要明白为什么要加互斥锁,不加互斥锁会导致什么问题,互斥锁保护的是什么变量。

1) 首先需要搞明白条件变量等待时传入的互斥锁,这个锁是保护的什么变量。这个锁不是用来保护条件变量的内部状态的,而是用来保护外部的共享变量的。退一步说,即使条件变量的内部状态需要锁定,完全可以在内部实现中维护一个锁,而没必要从外部传进来。

2) 不加锁会导致什么问题

首先要明白,这个锁传进去之后,还会进行解锁的。但是什么时候解锁呢?就是把条件变量等待这个事件放入到队列之后,线程挂起之前的这一时刻会解锁。

以代码讲解为例:

Thread A:
1: pthread_mutex_lock(&mutex); 
2: while (!ready) { 
3: pthread_cond_wait(&cond, &mutex); 
4: } 
5: pthread_mutex_unlock(&mutex);
Thread B:
1: ready = true;
2: pthread_cond_signal(&cond);

由于Thread B中没有加锁,导致Thread A和Thread B在调用的时候,有可能出现如下的调用顺序:

A1->A2->B1->B2->A3

那么可能导致的情况是什么呢?就是在ThreadA中本来是根据全局共享变量来判断是否等待信号量的,结果在等待之前发生线程切换导致先发送信号,之后,ThreadA再进行等待,此时就变成了无效等待,出现信号丢失的情况。

因此,在调进行条件变量等待的时候,必须传入互斥量,这个互斥量是保护判断是否需要等待的全局共享变量的。在发送信号的时候,也需要对全局变量进行保护。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值