1、条件变量

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

(1)创建和注销

  条件变量和互斥锁一样,都有静态&动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下:    
  pthread_cond_t   cond=PTHREAD_COND_INITIALIZER    
   
  动态方式调用pthread_cond_init()函数,API定义如下:    
  int   pthread_cond_init(pthread_cond_t   *cond,   pthread_condattr_t   *cond_attr)    
   
  尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。  
   
  注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:    
  int   pthread_cond_destroy(pthread_cond_t   *cond)    
   
(2)等待和激发   
   
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)    
   
等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回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()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。  
   
  激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。  

(3)互斥变量举例:生产者&消费者

(4)举例:

《举例1》

 /*************************************************************************
> File Name: pthread_cond1.c
> Summary: 条件变量应用---生产者&消费者 version1 单个生产者&单个消费者情形
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <pthread.h> // 链表作为共享数据,需要被互斥量保护
struct msg{
struct msg *next;
int num;
}; struct msg *head; // 静态初始化,一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p)
{
struct msg *mp;
for( ; ; )
{
pthread_mutex_lock(&mutex);
while(head == NULL) // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
{
// 一开始 阻塞等待,并解锁mutex
// 收到signal信号之后,解除阻塞,加锁mutex
pthread_cond_wait(&has_product, &mutex);
}
mp = head;
head = mp->next; // 消费掉一个产品
pthread_mutex_unlock(&mutex); printf("Consume %lu --- %d\n", pthread_self(), mp->num);
free(mp);
sleep(rand()%); // 休眠:为了打印效果明显
}
} void *producer(void *p)
{
struct msg *mp;
for(; ;)
{
mp = malloc(sizeof(struct msg));
mp->num = rand() % + ; // 模拟生产一个产品(1-1000之间的一个数字)
printf("Produce ---------------- %d\n", mp->num); // 加互斥锁:mp为共享数据
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product); // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
sleep(rand()%); // 休眠:为了打印效果明显
}
} int main()
{
pthread_t pid, cid;
srand(time(NULL)); // 创建线程
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&pid, NULL, consumer, NULL); // 等待回收
pthread_join(pid, NULL);
pthread_join(cid, NULL); return ;
}

运行结果(截取部分):

Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Produce ----------------
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---

《举例2》

 /*************************************************************************
> File Name: pthread_cond2.c
> Summary: 条件变量应用---生产者&消费者 version2 单个生产者&多个消费者情形
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <pthread.h> // 链表作为共享数据,需要被互斥量保护
struct msg{
struct msg *next;
int num;
}; struct msg *head; // 静态初始化,一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p)
{
struct msg *mp;
for( ; ; )
{
pthread_mutex_lock(&mutex);
while(head == NULL) // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
{
// 一开始 阻塞等待,并解锁mutex
// 收到signal信号之后,解除阻塞,加锁mutex
pthread_cond_wait(&has_product, &mutex); // 多个消费者线程都阻塞在这里
}
mp = head;
head = mp->next; // 消费掉一个产品
pthread_mutex_unlock(&mutex); printf("Consume %lu --- %d\n", pthread_self(), mp->num);
free(mp);
sleep(rand()%); // 休眠:为了打印效果明显
}
} void *producer(void *p)
{
struct msg *mp;
for(; ;)
{
mp = malloc(sizeof(struct msg));
mp->num = rand() % + ; // 模拟生产一个产品(1-1000之间的一个数字)
printf("Produce ---------------- %d\n", mp->num); // 加互斥锁:mp为共享数据
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product); // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
sleep(rand()%); // 休眠:为了打印效果明显
}
} int main()
{
pthread_t pid, cid;
srand(time(NULL)); // 创建线程
pthread_create(&pid, NULL, producer, NULL); // 创建多个消费者
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL); // 等待回收
pthread_join(pid, NULL);
pthread_join(cid, NULL); return ;
}

运行结果(截取部分):发现消费者线程id不同,即多个消费者

Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---

【Linux 线程】线程同步《三》的更多相关文章

  1. linux 线程的同步 三 (内存信号量的使用)

    信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已.但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆,关于用于进程间通 ...

  2. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  3. linux线程间同步方式总结梳理

    线程间一般无需特别的手段进行通信,由于线程间能够共享数据结构,也就是一个全局变量能够被两个线程同时使用.只是要注意的是线程间须要做好同步! 使用多线程的理由: 1. 一个是和进程相比,它是一种非常&q ...

  4. Linux线程间同步的几种方式

    信号量 信号量强调的是线程(或进程)间的同步:"信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在sem_wait的时候,就阻塞 ...

  5. Linux/Unix 线程同步技术之互斥量(1)

    众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...

  6. Linux的线程同步对象:互斥量Mutex,读写锁,条件变量

        进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...

  7. Linux系统编程(29)——线程间同步(续篇)

    线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变 ...

  8. linux线程间同步方式汇总

    抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin loc ...

  9. java并发编程(三)----线程的同步

    在现实开发中,我们或多或少的都经历过这样的情景:某一个变量被多个用户并发式的访问并修改,如何保证该变量在并发过程中对每一个用户的正确性呢?今天我们来聊聊线程同步的概念. 一般来说,程序并行化是为了获得 ...

  10. 深入Java线程管理(三):线程同步

    一. 引入同步: 有一个很经典的案例,即银行取款问题.我们可以先看下银行取款的基本流程: 1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. 2)用户输入取款金额. 3)系统判断账户金额是否大 ...

随机推荐

  1. cookies_ajax

    views def test_user(request): print('start') if request.method=='POST': print('goon_test_user') user ...

  2. List去重问题引出来的hashCode和equals方法

    一.List 里面是基本类型的去重问题 import java.util.ArrayList; import java.util.HashSet; import java.util.List; imp ...

  3. Python基本数据类型以及字符串

    基本数据类型                数字  int ,所有的功能,都放在int里            a1 = 123            a1 = 456                 ...

  4. Hbase数据读写流程

    From: https://blog.csdn.net/wuxintdrh/article/details/69056188 写操作: Client写入,存入Memstore,Memstore满则Fl ...

  5. js 正则函数初级

    1.test :正则匹配字符串,如果成功则返回true,若果失败则返回false 格式:/正则表达式/.test(字符串) 默认吗匹配规则,区分大小写:如果不区分大小写,则加修饰符 i 例子: < ...

  6. Java学习路线(转)

    原文:http://www.hollischuang.com/archives/489 一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http ...

  7. 尚硅谷springboot学习9-配置文件值注入

    首先让我想到的是spring的依赖注入,这里我们可以将yaml或者properties配置文件中的值注入到java bean中 配置文件 person: lastName: hello age: 18 ...

  8. Visual SVN Server备份脚本

    set tt=%date:~0,4%%date:~5,2%%date:~8,2% mkdir D:\SVN_BACKUP_%tt%\Repositories xcopy C:\Repositories ...

  9. 如何查看一个class文件是否正确

    今天碰到了个问题,左思右想就是找不出问题,试验多个路径来解决问题,错误依旧. 然后我拿到了现场的包,一个很大的问题让我忽略了,这个class文件用反编译程序打不开(jd-gui.exe),非常神奇,但 ...

  10. vmware搭建lnmp环境配置域名

    找到nginx配置文件,修改server_name 然后找到/etc/hosts文件 修改成如下 之后在Windows本地的C盘的hosts文件中添加解析 好了,这样就可以访问了 通往牛逼的路上,在意 ...