文档章节

Linux——互斥锁与条件变量(二)

KiteRunner
 KiteRunner
发布于 2014/06/02 09:07
字数 1544
阅读 1289
收藏 8
点赞 1
评论 0

三、对比上锁与等待

在上述的生产者-消费者问题中,我们在实现同步的时候还可以用以下的方法来实现,这里只是说明与上述实现中的不同之处。

1、首先,说明一下此版本中的相关特征:

在此版本中,消费者线程在生产者线程创建完毕后马上就创建,而不是等待所有生产者线程完成而终止后再创建。

2、再则,如何来实现:

A、Set_concurrency(..)中参量并发线程从nthreads变为nthreads+1;

B、同时,为了实现同步,我们必须设置一个consume_wait(int i)函数,其实现的功能是检测到对应i的pthread_mutex_unlock后,便启动consume对应的i判断部分。这里将这两个函数源代码实现呈现如下:

void consume_wait(int i){
    for(;;){
        pthread_mutex_lock(&shared.mutex);
        if(i < nput){
            pthread_mutex_unlock(&shared.mutex);
            return ;
        }
        pthread_mutex_unlock(&shared.mutex);
    } 
}
 
void *consume(void *arg){
    int i;
    for(i=0;i<nitems;i++){
        consume_wait(i);
        if(shared.buff[i] != i)
            printf("buff[%d] = %d\n",i,shared.buff[i]);
    }
    return NULL;
}

这里,特别说明一些关于consume_wait(int i)的实现及具体运行方式:

首先,当临界区出于未上锁时,consume_wait便调用pthread_mutex_lock(..)来上锁互斥锁,获得对临界区的所有权,这时,判断i<nput条件是否成立,以判断生产者线程是否产生了第i个条目。如果检测到之后,便可返回以通告consume可以处理第i个条目,同时解锁互斥锁。

然后,若i个条目未产生,这时,函数便一直循环,每次给互斥锁上锁又解锁,这样称为“轮询(polling)”,这对CPU是一种浪费,但着实可以实现同步问题。

四、条件变量(Condition):等待与信号发送

互斥锁用于上锁,条件变量用于等待。这两种不同类型的同步都是需要的。

       条件变量是类型为pthread_cond_t的变量,以下两个函数使用了这些变量:

#include<pthread.h>
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
均返回:若成功则为0,若出错则为正的Exxx值

其中第二个函数的名字中的“signal”一词指的不是Unix_SIGxxx信号。

这两个函数所等待或由之得以通知的“条件”,其定义由我们选择:我们在代码中测试这种条件。

每个条件变量总是有一个互斥锁与之关联。我们调用pthread_cond_wait等待某个条件为真时,还会指定其条件变量的地址和所关联的互斥锁的地址。

我们利用条件变量和互斥锁重新设计生产者-消费者同步问题,其改变如下:

全局变量声明:

#include "unpipc.h"
 
#define MAXNITEMS 1000000
#define MAXNTHREADS 100
 
int nitems;
int buff[MAXITEMS];
 
struct {
    pthread_mutex_t mutex;
    int nput;
    int nval;
}put ={PTHREAD_MUTEX_INITIALIZER};
 
struct {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int nready;
}nready={PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER};

把生产者变量和互斥锁收集到一个结构中;

把计数器、条件变量和互斥锁收集到一个结构中nready指的是对消费者已准备好的线程数;

main函数同上锁与等待时的main函数;

Produce和consume函数:

void *produce(void *arg){
    for(;;){
        pthread_mutex__lock(&put.mutex);
        if(put.nput >= nitems){
            pthread_mutex_unlock(&put.mutex);
            return NULL;
        }
        buff[put.nput]=put.nval;
        put.nput++;
        put.nval++;
        pthread_mutex_unlock(&put.mutex);
        pthread_mutex_lock(&nready.mutex);
        if(nready.nready == 0){
            pthread_cond_signal(&nready.cond);
        }
        nready.nready++;
        pthread_mutex_unlock(&nready.mutex);
    }
}
 
void *consume(void *arg){
    int i;
    for(i=0;i<nitems;i++){
        pthread_mutex_lock(&nready.mutex);
        while(nready.nready == 0)
            pthread_cond_wait(&nready.cond,&nready.mutex);
        nready.nready--;
        pthread_mutex_unlock(&nready.mutex);
        if(buff[i]!=i){
        printf("buff[%d]=%d\n",i,buff[i]);
        }
    }
    return NULL;
}

这里的分析很重要:

Produce函数中的for语句部分前半部分实现的是:当生产者往数组buff中放置一个新条目时,我们改用put.mutex来为临界区上锁。

For语句后半部分实现的是通知消费者,给用来统计由消费者处理的条目数的计数器nready.nready1。在加1之前,如果该计数器的值为0,就调用pthread_cond_signal唤醒可能正等待其值变为非零的任意线程(如消费者)。现在可以看出与该计数器关联的互斥锁和条件变量的相互作用。该计数器在生产者和消费者之间共享,因此,只有锁住与之关联的互斥锁(nready.mutex)时才能访问它。与之关联的条件变量则用于等待和发送信号。

对于消费者,消费者只是等待计数器nready.nready变为非零。既然该计数器是在所有的生产者和消费者之间共享的,那么只有锁住与之关联的互斥锁(nready.mutex)时才能测试它的值。如果在锁住该互斥锁期间该计数器的值为0,我们就调用pthread_cond_wait进入睡眠。该函数原子地执行以下两个动作:

A、给互斥锁nready.mutex解锁;

B、把调用线程投入睡眠,直到另外某个线程就本条件变量调用pthread_cond_signal。同时,pthread_cond_wait在返回前重新给互斥锁nready.mutex上锁。

五、条件变量:定时等待和广播

通常pthread_cond_signal只唤醒等待在相应条件变量上的一个线程。在某些情况下,一个线程认定有多个其他线程应被唤醒,这时它可以调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。

#include<pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cptr);
int pthread_cond_timewait(pthread_cond_t *cptr,pthread_mutex_t *mptr,const struct timespc *abstime);

对于pthread_cond_timewait(...),其允许线程就阻塞时间设置一个限制值。Abstime参数是一个timespec结构体,该结构体指定这个函数必须返回的时间,即便当时相应的条件变量还没收到信号,如果发生这种超时情况,该函数返回ETIMEDOUT错误。该时间值是绝对时间。而不是时间差。

六、互斥锁和条件变量的属性

在前面的互斥锁和条件变量的讲解中,我们用两个常量PTHREAD_MUTEX_INITIALIZERPTHREAD_COND_INITIALIZER来初始化它们。有这种方式初始化的互斥锁和条件变量具备默认属性,不过我们还能以非默认属性来初始化它们。

#include<pthread.h>
 
int pthread_mutex_init(pthread_mutex_t *mptr,const pthread_mutex_mutexattr_t *attr);
int pthread_mutex_destory(pthread_mutex_t *mptr);
int pthread_cond_init(pthread_cond_t *cptr,const pthread_cond_condattr_t *attr);
int pthread_cond_destory(pthread_cond_t *cptr);


© 著作权归作者所有

共有 人打赏支持
KiteRunner
粉丝 3
博文 20
码字总数 14816
作品 0
成都
Linux——互斥锁与条件变量(一)

为允许在线程或进程间共享数据,同步通常是必需的。互斥锁和条件变量是同步的基本组成部分。 使用互斥锁和条件变量的经典例子:生产者-消费者问题。在本例中,使用多个线程而不是多个进程,因...

KiteRunner
2014/06/01
0
0
Linux多线程编程

一 线程(也称为轻量级的进程) 1.1、线程基本特点 a)线程依赖于进程而存在,在一个进程里面开出的多个线程共享同一进程空间,这样利用多线程实现多任务时,就能避免因为大量频繁的进程间切...

wannneg
2016/04/15
29
0
0-linux 编程学习笔记导航

学习交流群: Linux 环境编程 610441700 说明:本系列文章并不能取代 《APUE》这本旷世之作,文章中难免有错误与不足之处,希望读者们遇到有疑问的地方可以加群互相交流,共同进步。写这一系...

q1007729991
2016/10/09
0
0
漏洞预警 Linux内核出现多个漏洞

  漏洞概述   Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。漏洞发生在 Linux 内核中,可造成越界访问或释放后再使用的安全问题。   漏洞影...

FreeBuf
05/18
0
0
Linux多线程及线程间同步

1、进程和线程的区别 进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程...

陈国成
2017/06/10
0
0
Linux 的 进程/线程 通信方式总结

linux系统中的进程通信方式主要以下几种: PIPE(FIFO) 消息队列 信号量(Semaphore) 共享存储 SOCKET 同一主机上的进程通信方式 UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(...

大数据之路
2012/10/05
0
0
Linux多线程编程四(条件变量)

前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互...

wannneg
2016/04/16
35
0
Linux多线程Pthread学习小结

简介 POSIX thread 简称为pthread,Posix线程是一个POSIX标准线程.该标准定义内部API创建和操纵线程. 作用 线程库实行了POSIX线程标准通常称为pthreads.pthreads是最常用的POSIX系统如Linux...

长平狐
2013/01/06
40
0
Linux多线程Pthread学习小结

简介 POSIX thread 简称为pthread,Posix线程是一个POSIX标准线程.该标准定义内部API创建和操纵线程. 作用 线程库实行了POSIX线程标准通常称为pthreads.pthreads是最常用的POSIX系统如Linux...

晨曦之光
2012/03/02
144
0
Linux 多线程

线程的优点: 减少系统调度开销,不占有独立的资源,切换速度快,执行效率高。 线程间通信方便,可共享资源。 改善程序设计结构,功能复杂的进程可以分为多个独立的线程分别执行,模块性更强...

文艺小青年
2017/11/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Kafka设计解析(一)- Kafka背景及架构介绍

原创文章,转载请务必将下面这段话置于文章开头处。(已授权InfoQ中文站发布) 本文转发自技术世界,原文链接 http://www.jasongj.com/2015/03/10/KafkaColumn1 摘要   Kafka是由LinkedI...

mskk
7分钟前
0
0
使用Service Mesh整合您的微服务架构

在微服务架构的世界中,它正在达到这样的程度,即管理系统的复杂性对于利用它带来的好处变得至关重要。 目前,如何实现这些微服务不再是一个问题,因为有很多可用的框架(Spring Boot,Vert....

xiaomin0322
11分钟前
0
0
看看 LinkedList Java 9

终于迎来了 LinkedList 类,实现的接口就有点多了 Serializable, Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>。LinkedList是一个实现了List接口和Deque接口的双端链......

woshixin
29分钟前
0
0
算法 - 冒泡排序 C++

大家好,我是ChungZH。今天我给大家讲一下最基础的排序算法:冒泡排序(BubbleSort)。 冒泡排序算法的原理如下: 比较相邻的元素。如果第一个比第二个大(可以相反),就交换他们两个。 对每...

ChungZH
32分钟前
0
0
jquery ajax request payload和fromData请求方式

请求头的不同 fromData var data = { name : 'yiifaa'};// 提交数据$.ajax('app/', { method:'POST', // 将数据编码为表单模式 contentType:'application/x-ww...

lsy999
34分钟前
0
0
阿里P7架构师,带你点亮程序员蜕变之路

前言: Java是现阶段中国互联网公司中,覆盖度最广的研发语言。 掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地。 有不少朋友问,成为Java架...

Java大蜗牛
36分钟前
1
0
Ecstore 在没有后台管理界面(维护)的情况如何更新表的字段

window 系统: 切换到:app\base 目录下: C:\Users\qimh>d: D:\>cd D:\WWW\huaqh\app\base 执行:D:\WWW\huaqh\app\base>cmd update linux 系统: 1># cd /alidata/www.novoeshop.com/app/......

qimh
40分钟前
0
0
设计模式-策略模式

策略模式 解释 对工厂模式的再次封装,使用参数控制上下文信息(将工厂返回的实例赋值给context field) 不会返回bean实例,只是设置对应的条件 调用context的方法(调用field的方法) 用户只...

郭里奥
43分钟前
0
0
python使用有序字典

python自带的collections包中有很多有用的数据结构可供使用,其中有个叫OrderedDict类,它可以在使用的时候记录元素插入顺序,在遍历使用的时候就可以按照原顺序遍历。 a = {"a":1,"b"...

芝麻糖人
今天
0
0
RestTemplate HttpMessageConverter

RestTemplate 微信接口 text/plain HttpMessageConverter

微小宝
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部