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. Django--ORM(模型层)-重点

    一.ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库, 通过简单的配置就可以轻松更换数据库,这极大的减轻了开发 ...

  2. 【386】operator 的 itemgetter、slice、and_、or_

    itemgetter 用来获取数组中指定索引的元素 from operator import itemgetter itemgetter(1, 3, 5)('ABCDEFG') output: ('B ...

  3. map和hasmap的区别

    MAP接口的定义如下: public interface MAP< k , v>      Key 到value 的映射 ,Key不允许重复,每一个key只能映射一个value . Has ...

  4. JAVAWEB 一一SpringMVC(XML配置)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="2 ...

  5. web前端基础知识!

    [HTML文档的基本结构和语法][基本结构]: <HTML> HTML 文件开始 <HEAD> HTML 文件的头部开始 <title> 网页的标题</tit ...

  6. HttpWatch手把手图解教程

    HttpWatch手把手图解教程,提供HttpWatch下载,教您安装使用,一步到位 一 HttpWatch下载: HttpWatchProv7.2.13 破解版(带正版key) 授权:共享软件 大小 ...

  7. ceph部署手册

    CentOS7.2部署Luminous版Ceph-12.2.0 在CentOS7.2上安装部署Luminous版Ceph-12.2.0.由于ceph的Luminous版本默认使用bluestore作为 ...

  8. AJAX简单实例

    越用AJAX越觉得它的强大.好用. 平常我们提交表单,是直接通过action属性,直接向后台提交数据. 我们也可以用AJAX向后台提交数据.例如: 这是一个表单,两个字段:notice,scort,保 ...

  9. Spring @ControllerAdvice @ExceptionHandler 全局处理异常

    对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚. 如此一来, ...

  10. java中File类的常用方法总结

    java中File类的常用方法 创建: createNewFile()在指定的路径创建一个空文件,成功返回true,如果已经存在就不创建,然后返回false. mkdir() 在指定的位置创建一个此抽 ...