1.原理 假设有两个线程同时访问一个全局变量 n,这个全局变量的初始值等于0。

Int  n = 0 ;

消费者线程 A 进入临界区,访问 n,A 必须等到 n 大于 0 才能接着往下执行,如果 n== 0,那么 A 将一直等待。

还有一个生产者线程 B,B 进入临界区,修改 n 的值,使得 n >0,当 n > 0 时,B 通知等待 n > 0 的消费者线程A。A 被 B 通知之后就可以接着往下执行了。

以上情况造成死锁:

当 A 进入临界区时,其他线程不能进入临界区,意味着 B 没有机会去修改 n, n 的值一直为 0,不满足A 继续执行的条件(n > 0),A 只能一直等待。

消费者进程拿到互斥锁 --> 进入临界区 --> 发现共享资源 n 不满足继续执行的条件(n> 0) --> 等待 n > 0

消费者进程占有互斥锁 --> 生产者进程无法进入临界区 --> 无法修改 n 的值 --> 生产者等待消费者释放互斥锁

解决死锁的方案就是采用条件变量。

通常情况下,对共享资源(比如 n)保护要用到锁操作,当一个进程进入临界区时会拿到互斥锁(lock 操作),然后其他进程拿不到互斥锁,也就无法进入临界区,因此当进程进入临界区,发现共享资源不满足继续向下执行的条件(n > 0)时,就应该释放锁,让其他进程修改共享资源,以满足自己所需的执行条件。

消费者进入临界区 --> 共享变量不满足继续向下执行的条件 --> 消费者等待在条件变量 --> 释放互斥锁 --> 生产者进入临界区 --> 修改条件变量 --> 生产者通知消费者:现在有多的资源了,快来使用 --> 消费者再次拿互斥锁 --> 消费资源 --> 释放互斥锁。如果有多个消费者进程等待在条件变量上,就可以形成等待队列。

生产者和消费者模型中互斥锁和条件变量的使用流程图如下,其中蓝色代表消费者的执行流,红色是生产者的执行流。

2.使用方法
         条件变量的使用主要有以下五个函数:

/* 初始化一个条件变量 */
int pthread_cond_init (pthread_cond_t* cond, pthread_condattr_t *cond_attr);

/* 销毁一个条件变量 */
int pthread_cond_destroy(pthread_cond_t* cond);

/* 令一个消费者等待在条件变量上 */
int pthread_cond_destroy(pthread_cond_t* cond);

/* 生产者通知等待在条件变量上的消费者 */
int pthread_cond_signal(pthread_cond_t* cond);

/* 生产者向消费者广播消息 */
int pthread_cond_broadcast(pthread_cond_t* cond);

消费者等待条件的伪代码:

pthread_mutex_lock(&mutex); // 拿到互斥锁,进入临界区
while( 条件为假)
pthread_cond_wait(cond, mutex); // 令进程等待在条件变量上
修改条件
pthread_mutex_unlock(&mutex); // 释放互斥锁

生产者通知消费者的伪代码:

pthread_mutex_lock(&mutex); // 拿到互斥锁,进入临界区
设置条件为真
pthread_cond_signal(cond); // 通知等待在条件变量上的消费者
pthread_mutex_unlock(&mutex); // 释放互斥锁

以下是示例程序,演示了互斥锁和条件变量配合使用方法,由于是在Linux下写的程序,所以注释全是英文的。

condition_test.c:

/***************************************************************
* Copyright (C) 2016 chengonghao
* All rights reserved.
*
* chengonghao@yeah.net
***************************************************************/
#include <unistd.h>
#include <pthread.h>

#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 1

pthread_mutex_t g_mutex ;
pthread_cond_t g_cond ;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT] ;
int share_variable = 0 ;// this is the share variable, shared by consumer and producer

void* consumer( void* arg )
{
int num = (int)arg ;
while ( 1 )
{
/******* critical section begin *******/
pthread_mutex_lock( &g_mutex ) ;

// if share_variable == 0, means consumer shell stop here
while ( share_variable == 0 )
{
printf( "consumer %d begin wait a condition...\n", num ) ;
// put a thread blocked ont a condition variable( here is g_cond),
// and unlock the mutex( here is g_mutex )
pthread_cond_wait( &g_cond, &g_mutex ) ;
}
// here means n != 0 and consumer can goes on
// consumer consumed shared variable, so the number of shared variable shell minus
printf( "consumer %d end wait a condition...\n", num ) ;
printf( "consumer %d begin consume product\n", num ) ;
-- share_variable ;

pthread_mutex_unlock( &g_mutex ) ;
/******** critial section end *********/
sleep( 1 ) ;
}

return NULL ;
}

void* producer( void* arg )
{
int num = (int)arg ;
while ( 1 )
{
/******* critical section begin *******/
pthread_mutex_lock( &g_mutex ) ;

// produce a shared variable
printf( "producer %d begin produce product...\n", num ) ;
++ share_variable ;
printf( "producer %d end produce product...\n", num ) ;
// unblock threads blocked on a condition variable( here is g_cond )
pthread_cond_signal( &g_cond ) ;
printf( "producer %d notified consumer by condition variable...\n", num ) ;
pthread_mutex_unlock( &g_mutex ) ;

/******** critial section end *********/
sleep( 5 ) ;
}

return 1 ;
}

int main( void )
{
// initiate mutex
pthread_mutex_init( &g_mutex, NULL ) ;
// initiate condition
pthread_cond_init( &g_cond, NULL ) ;

// initiate consumer threads
for ( int i = 0; i < CONSUMERS_COUNT; ++ i )
{
pthread_create( &g_thread[i], NULL, consumer, (void*)i ) ;
}
sleep( 1 ) ;
// initiate producer threads
for ( int i = 0; i < PRODUCERS_COUNT; ++ i )
{
pthread_create( &g_thread[i], NULL, producer, (void*)i ) ;
}
for ( int i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++ i )
{
pthread_join( g_thread[i], NULL ) ;
}

pthread_mutex_destroy( &g_mutex ) ;
pthread_cond_destroy( &g_cond ) ;
}

编译程序:

cgh@ubuntu:~/condition_test$ gcc condition_test.c -o test –lpthread

运行程序:

1.      第一个框,消费者 1 和0 发现share_variable == 0,于是先后等待在条件变量上;

2.      第二个框,生产者 0 开始生产共享变量,即 ++ share_variable,然后通知等待在条件变量上的消费者;

3.      第三个框,消费者 1 被生产者唤醒,开始消费共享变量,即– share_variable;

4.      第四个框,生产者 0 继续生产共享变量,++ share_variable,然后通知等待在条件变量上的消费者;

5.      第五个框,消费者 0 被唤醒,开始消费共享变量,即– share_variable;

以此类推,以上描述简化了拿锁和释放锁的过程,可以结合上面的流程图来理解代码。
---------------------
作者:chengonghao
来源:CSDN
原文:https://blog.csdn.net/chengonghao/article/details/51779279
版权声明:本文为博主原创文章,转载请附上博文链接!

16、cgminer学习之:pthread_mutex_init和pthread_cond_init的更多相关文章

  1. 2018面向对象程序设计(Java)第16周学习指导及要求

    2018面向对象程序设计(Java)第16周学习指导及要求(2018.12.13-2018.12.16)   学习目标 (1) 掌握线程概念: (2) 掌握线程创建的两种技术: (3) 理解和掌握线程 ...

  2. 《Think Python》第16章学习笔记

    目录 <Think Python>第16章学习笔记 16.1 Time 16.2 纯函数(Pure functions) 16.3 修改器(Modifiers) 16.4 原型 vs. 方 ...

  3. 2019面向对象程序设计(Java) 第16周学习指导及要求

    2019面向对象程序设计(Java)第16周学习指导及要求 (2019.12.13-2019.12.16) 学习目标 (1) 掌握Java应用程序的打包操作: (2) 掌握线程概念: (3) 掌握线程 ...

  4. 16、cgminer学习之:popen函数和system函数详解(执行系统命令)

    1.popen函数我们先用man指令查一下popen函数: 函数说明: (1)popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令. (2) ...

  5. 2016.8.16 JQuery学习记录

    1.$(document).ready(function(){}); 这个函数会在浏览器加载完页面之后,尽快执行: 2.所有的JQuery函数用有个$开始表示,All jQuery functions ...

  6. 2015第16周六学习java建议

    学习Java 建议: 尽量用 google 查找技术资料. 有问题在 stackoverflow 找找,大部分都已经有人回答. 多看官方的技术文档. ibm developerworkers 的文章质 ...

  7. Linux内核(16) - 高效学习Linux内核

    世界悲结束了,章鱼哥也退役了,连非诚勿扰中的拜金女也突然的少了很多.这本<Linux内核修炼之道>在卓越.当当.china-pub上也已经开卖了,虽然是严肃文学,但为了保证流畅性,大部分文 ...

  8. [Java开发之路](16)学习log4j日志

    1. 新建一个Javaproject.导入Jar包(log4j-1.2.17.jar) Jar包下载地址:点击打开链接 2. 配置文件:创建并设置log4j.properties # 设置 log4j ...

  9. 201772020113 李清华《面向对象程序设计(java)》第16周学习总结

    1.实验目的与要求 (1) 掌握线程概念: (2) 掌握线程创建的两种技术: (3) 理解和掌握线程的优先级属性及调度方法: (4) 掌握线程同步的概念及实现技术: 2.实验内容和步骤 实验1:测试程 ...

随机推荐

  1. 手把手教你安装Navicat——靠谱的Navicat安装教程

    Navicat是一款轻量级的用于MySQL连接和管理的工具,非常好用,使用起来方便,简洁.下面讲讲其安装的过程. 1.进入navicat官网,选择Navicat for MySQL,然后点击进行下载即 ...

  2. 应对加密js的三种方法

    经常遇到网页在登录后会对用户输入的帐号和密码通过js进行加密,导致模拟登录这类网站时受到阻碍 这里小记一下当前解决该问题的三种方法 1.利用python实现js同等加密. 2.利用selenium模拟 ...

  3. Python基础班培训视频课程

    课程目录:│  ├─第01天视频│  │      01-课程介绍.avi│  │      02-什么是操作系统.avi│  │      03-生活中的操作系统.avi│  │      04-操 ...

  4. 一个Web报表项目的性能分析和优化实践(六):设置MySQL的最大连接数(max_connections)

    在上一篇文章中"一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例"提到,项目中新增几个数据库 ...

  5. 使用Xcode的Targets来管理开发和生产版本的构建

    如何创建一个新的Target 如何在Xcode中创建一个开发的target?我使用示例项目“todo”引导您一步一步完成整个过程..您也可以使用自己的项目并按照步骤: 1. 在项目的导航面板进入项目设 ...

  6. CSUOJ 1635 Restaurant Ratings

    1635: Restaurant Ratings Time Limit: 1 Sec  Memory Limit: 128 MB Description A famous travel web sit ...

  7. 转:IOS的推送。是一个强大的功能

    IOS下我们很多人天天开着 邮件推送 微信推送 QQ推送 微博推送 新浪微博推送,安卓敢吗? 五个后台进程消耗电.流量谁敢这么做?现在安卓也出了推送,但绝对是伪推送.实际是挂了个进程,关掉了就收不到. ...

  8. spring set注入

    上篇文章说到了构造器注入.可是有时候构造器注入并非非常好用,如今来看下set注入. 构造器注入博客地址:http://blog.csdn.net/luckey_zh/article/details/4 ...

  9. Android自己定义动态布局 — 多图片上传

    Android自己定义动态布局 - 多图片上传 本文介绍Android中动态布局加入图片,多图片上传. 项目中效果图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5 ...

  10. ios代理的使用,正向传值,逆向传值

    #import <UIKit/UIKit.h> #import "SubViewController.h" @interface ViewController : UI ...