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. JAVA学习书籍

    1 java编程思想(第四版) 2 head first servlet &jsp

  2. Lightroom 学习笔记

    16.8.28  白平衡:      夕阳照片,色温高大

  3. 网络场景图WebScene介绍

    网络场景图的创建同样需要ID,然后再放入3维的地图底图中. 1.创建一个新的网络场景图 "esri/WebScene" var scene = new WebScene({ por ...

  4. CentOS7 PXE安装批量安装操作系统

    1.安装相关软件 yum -y install tftp-server httpd dhcp syslinux 2.配置DHCP cp /usr/share/doc/dhcp-4.2.5/dhcpd. ...

  5. 怎样更好的设计android图标,拉伸不变形等等系列长处,并且减小apk大小

    android mvp框架:dileber(https://github.com/dileber/dileber.git) 继续为大家介绍android mvp开源框架 dileber  今天主要是字 ...

  6. [ES2017] Iterate over properties of an object with ES2017 Object.entries()

    The Object.entries() function is an addition to the ECMAscript scpec in Es2017. This allows us to it ...

  7. 一种基于RGB空间的对照度增强的filter

    今天看前辈博客的时候看到一种新的基于RGB颜色空间的image contrast enhance filter 流浪的鱼link: http://blog.csdn.net/jia20003/arti ...

  8. poj1328Radar Installation 贪心

    Radar Installation Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 64472   Accepted: 14 ...

  9. java poi操作excel示例代码

    import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

  10. assert增强宏的实现

    作者:朱金灿 来源:http://blog.csdn.net/clever101 标准c的assert宏和MFC的ASSERT宏都不支持输出太多的信息.今天实现了一个assert增强宏,可以输出更多的 ...