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. 爬虫--requests模块高级(代理和cookie操作)

    代理和cookie操作 一.基于requests模块的cookie操作 引言:有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests ...

  2. CSS GRID ESSENTIALS

    CSS GRID ESSENTIALS Review At this point, we've covered a great deal of different ways to manipulate ...

  3. 几道关于springboot、springCloud的面试题。

    什么是springboot 用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(propertites或yml文件) 创建独立的spring引用程序main方法运行 嵌入的tom ...

  4. 关于STRUCT优化的一个点

    在西山居的这篇U3D cheatsheet中,提到: c12. 确保 struct 实现了 Equals() 和 GetHashCode() 这怎么理解? 首先,看下system.object.equ ...

  5. Redis使用认证密码登录

    Redis默认配置是不需要密码认证的,也就是说只要连接的Redis服务器的host和port正确,就可以连接使用.这在安全性上会有一定的问题,所以需要启用Redis的认证密码,增加Redis服务器的安 ...

  6. 拓展Scene视图——场景编辑Vector2/3

    Test.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class ...

  7. MIME sniffing攻击

    基于IE的MIME sniffing功能的跨站点脚本攻击 IE有一个特性,那就是在将一个文件展示给用户之前会首先检查文件的类型,这乍看起来并没什么问题,但实际上这是相当危险的,因为这会允许IE执行图片 ...

  8. css美化select标签,兼容ie10 ie10+,chrome。但不支持ie9 ie9-

    让ie9 ie9+ 和非ie的浏览器加载这个hack.ie8,ie8- 就用自己的默认样式 <!-- email:416960428@qq.com author:李可 --> <!- ...

  9. godep 包管理

    go get -u -v github.com/tools/godep  godep save

  10. python 删除模块

    import systry:    import librabbitmqexcept Exception:    passelse:    version = getattr(librabbitmq, ...