• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

从零学习多线程第三篇__互斥量与条件变量的应用

Linux admin 5年前 (2015-05-30) 1350次浏览 0个评论 扫描二维码

 条件变量

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

条件变量的创建与注销

条件变量的创建也是有两种方式:静态方式与动态方式。
条件变量的数据结构如下:

typedef union
{
struct
{
int __lock;
unsigned int __futex;
__extension__ unsigned long long int __total_seq;
__extension__ unsigned long long int __wakeup_seq;
__extension__ unsigned long long int __woken_seq;
void *__mutex;
unsigned int __nwaiters;
unsigned int __broadcast_seq;
} __data;
char __size[__SIZEOF_PTHREAD_COND_T];
__extension__ long long int __align;
} pthread_cond_t;

静态方式:与互斥量一样也存在默认条件变量 PTHREAD_COND_INITIALIZER 常量

pthread_cond_t cond=PTHREAD_COND_INITIALIZER

动态方式:通过调用 API 函数 pthread_cond_init 函数,函数原型如下:

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

创建成功会返回 0,失败则返回错误代码。尽管 POSIX 标准中为条件变量定义了属性,但在 LinuxThreads 中没有实现,因此 cond_attr 值通常为 NULL,且被忽略。

线程注销:API 函数原型如下:

int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_destroy 函数可以用来摧毁所指定的条件变量,同时将会释放所给它分配的资源。调用该函数的进程也并不要求等待在参数所指定的条件变量上。因为 Linux 实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。

线程的等待与激活

线程等待

线程等待 API 提供了两个 API 函数:

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 struct timespec *abstime)

注释:前两个参数分别是指向条件变量的指针和指向互斥量的指针,第二个函数的第三个变量是指向 timespec 结构体的指针,意思是为等待的线程提供倒计时,若时间到了还未被唤醒则返回 ETIMEOUT,结束等待。结构体的定义如下:

struct timespec
{
__time_t tv_sec;        /* Seconds.  */
long int tv_nsec;       /* Nanoseconds.  */
};

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求 pthread_cond_wait()(或 pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex 互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP)且在调用 pthread_cond_wait()前必须由本线程加锁 pthread_mutex_lock(),而在更新条件等待队列以前,mutex 保持锁定状态,并在线程挂起进入等待前解锁。

线程的激活

/* Wake up one thread waiting for condition variable COND.  */
extern int pthread_cond_signal (pthread_cond_t *__cond)
__THROWNL __nonnull ((1));

/* Wake up all threads waiting for condition variables COND.  */
extern int pthread_cond_broadcast (pthread_cond_t *__cond)
__THROWNL __nonnull ((1));

注释:二者区别在于第一个只会一次唤醒一个线程但是第二个会唤醒所有等待线程。

其他

pthread_cond_wait()和 pthread_cond_timedwait()都被实现为取消点,因此,在该处等待的线程将立即重 新运行,在重新锁定 mutex 后离开 pthread_cond_wait(),然后执行取消动作。也就是说如果 pthread_cond_wait()被 取消,mutex 是保持锁定状态的,因而需要定义退出回调函数来为其解锁。

条件变量与互斥量例子解析

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/

void *thread1(void *);
void *thread2(void *);

int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;

pthread_create(&t_a,NULL,thread2,(void *)NULL);/*创建进程 t_a*/
pthread_create(&t_b,NULL,thread1,(void *)NULL); /*创建进程 t_b*/
pthread_join(t_b, NULL);/*等待进程 t_b 结束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}

void *thread1(void *junk)
{
for(i=1;i<=9;i++)
{
pthread_mutex_lock(&mutex);/*锁住互斥量*/
if(i%3==0)
pthread_cond_signal(&cond);/*条件改变,发送信号,通知 t_b 进程*/
else
printf("thead1:%d\n",i);
pthread_mutex_unlock(&mutex);/*解锁互斥量*/

sleep(1);
}

}

void *thread2(void *junk)
{
while(i<9)
{
pthread_mutex_lock(&mutex);

if(i%3!=0)
pthread_cond_wait(&cond,&mutex);/*等待*/
printf("thread2:%d\n",i);
pthread_mutex_unlock(&mutex);

sleep(1);
}
}

注释:主线程在运行到 pthread_jion 时被挂起直到子线程运行结束,子线程 t_b 运行但是只有遇到数字等于 3 的倍数时才会输出,所以第一次在遇到全局变量 i 不等于 3 倍数时使用
使用 pthread_cond_wat 使其阻塞,线程 t_a 继续运行,如此循环输出直到子线程运行结束实现线程的同步!

运行结果截图:


Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明从零学习多线程第三篇 __ 互斥量与条件变量的应用
喜欢 (0)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!