linux网络编程之posix 线程(四):posix 条件变量与互斥锁 示例生产者--消费者问题

http://blog.csdn.net/jnu_simba/article/details/9129939

一、posix 条件变量

一种线程间同步的情形:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。


在pthread库中通过条件变量(Condition Variable)来阻塞等待一个条件,或者唤醒等待这个条件的线程。Condition Variable用pthread_cond_t类型的变量表示,和Mutex的初始化和销毁类似,pthread_cond_init函数初始化一个Condition Variable,attr参数为NULL则表示缺省属性,pthread_cond_destroy函数销毁一个Condition Variable。如果ConditionVariable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于用pthread_cond_init函数初始化并且attr参数为NULL。


一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回

注意:3个操作是原子性的操作,之所以一开始要释放Mutex,是因为需要让其他线程进入临界区去更改条件,或者也有其他线程需要进入临界区等待条件。


一个线程可以调用 pthread_cond_signal 唤醒在某个Condition Variable上等待的第一个线程,也可以调用 pthread_cond_broadcast 唤醒在这个Condition Variable上等待的所有线程。


上面的函数具体可以man 一下。


二、条件变量使用规范

(一)、等待条件代码
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);


假设第一段用于消费者线程,第二段用于生产者线程。为什么消费者线程要用while 而不是if 就可以呢?在man pthread_cond_wait 有一句:If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was not interrupted, or it shall return zero due to spurious wakeup.

即是说如果正在等待条件变量的一个线程收到一个信号,从信号处理函数返回的时候线程应该重新等待条件变量就好象没有被中断一样,或者被虚假地唤醒返回0。如果是上述情形,那么其实条件并未被改变,那么此时如果没有继续判断一下条件的真假就继续向下执行的话,修改条件将会出现问题,所以需要使用while 循环再判断一下,如果条件还是为假必须继续等待。

注:在多处理器系统中,pthread_cond_signal 可能会唤醒多个等待条件的线程,这也是一种spurious wakeup。


当生产者线程较多,即生产得比较快,在这边假设是无界的缓冲区(比如链表),可以不停地生产,使用pthread_cond_signal  发出通知的时候,如果此时没有消费者线程在等待条件,那么这个通知将被丢弃,但也不影响整体代码的执行,没有消费者线程在等待,说明产品资源充足,即while 判断失败,不会进入等待状态,直接消费产品(即修改条件)。


三、生产者消费者问题 

C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

#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 nready = 0;

void *consume(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        while (nready == 0)
        {
            printf("%d begin wait a condtion ...\n", num);
            pthread_cond_wait(&g_cond, &g_mutex);
        }

        printf("%d end wait a condtion ...\n", num);
        printf("%d begin consume product ...\n", num);
        --nready;
        printf("%d end consume product ...\n", num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }
    return NULL;
}

void *produce(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        printf("%d begin produce product ...\n", num);
        ++nready;
        printf("%d end produce product ...\n", num);
        pthread_cond_signal(&g_cond);
        printf("%d signal ...\n", num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }
    return NULL;
}

int main(void)
{
    int i;

    pthread_mutex_init(&g_mutex, NULL);
    pthread_cond_init(&g_cond, NULL);


    for (i = 0; i < CONSUMERS_COUNT; i++)
        pthread_create(&g_thread[i], NULL, consume, (void *)i);

    sleep(1);

    for (i = 0; i < PRODUCERS_COUNT; i++)
        pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);

    for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++)
        pthread_join(g_thread[i], NULL);

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

    return 0;
}


在上面程序中,++nready 就当作是生产产品了,--nready就当作是消费产品了,跟前面文章所不同的是,这里没有缓冲区大小的概念,可以当作是无界缓冲区,生产者可以一直生产,但消费者只有当有产品的时候才能消费,否则就得等待,等待结束的条件就是nready > 0;上面也说过当生产得比较快(生产者线程多)的时候,也有可能消费者线程一直不存在等待的状态,因为nready 的值很大,即产品资源很多。现在设置的是2个消费者线程和1个生产者线程,所以动态输出来看一般是2个消费者线程轮流等待。


参考:

《linux c 编程一站式学习》

《UNP》


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3-V2-类和对象 -- const内联 静态成员 友元

const修饰成员函数 在成员函数后面加一个const, const修饰this指针指向的对象, 保证调用这个const成员函数的对象在函数内不会被改变 注意:成员函数如果不修改成员变量,则加上const,成员函数如果要修改成员变量,此时就不能给其加上const修饰了 1.const对象不能调用非const…

C语言 二级指针内存模型混合实战

http://www.cnblogs.com/zhanggaofeng/p/5485833.html //二级指针内存模型混合实战 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h>//将内存模型①和内存模型②的数据拷贝到内存模型③ char ** threemodel(ch…

Linux 网络编程八(epoll应用--大并发处理)

http://www.cnblogs.com/zhanggaofeng/p/5901316.html //头文件 pub.h #ifndef _vsucess#define _vsucess#ifdef __cplusplus extern "C" {#endif //服务器创建socket int server_socket(int port);//设置非阻塞 int setnonblock(int st);//接收客户端socket int ser…

【数据结构与算法】内部排序之三:堆排序(含完整源码)

转载请注明出处&#xff1a;http://blog.csdn.net/ns_code/article/details/20227303 前言 堆排序、快速排序、归并排序&#xff08;下篇会写这两种排序算法&#xff09;的平均时间复杂度都为O&#xff08;n*logn&#xff09;。要弄清楚堆排序&#xff0c;就要先了解下二叉堆这…

模线性方程(中国剩余定理+扩展中国剩余定理)

已知一系列除数和模数,求最小的满足条件的数 我们先考虑一般的情况&#xff0c;即模数不互质。&#xff08;扩展中国剩余定理&#xff09; 我们考虑两个方程的情况 x%MR xk1∗MRxk1 * MRxk1∗MR x%mr xk2∗mrxk2 * mrxk2∗mr 所以k1∗MRk2∗mrk1 * MRk2 * mrk1∗MRk2∗mr 即…

【数据结构】(面试题)使用两个栈实现一个队列(详细介绍)

http://blog.csdn.net/hanjing_1995/article/details/51539578 使用两个栈实现一个队列 思路一&#xff1a; 我们设定s1是入栈的&#xff0c;s2是出栈的。 入队列&#xff0c;直接压到s1即可 出队列&#xff0c;先把s1中的元素倒入到s2中&#xff0c;弹出s2中的栈顶元素&#x…

C++::探索对象模型

前面我们已经知道, 在没有虚函数的时候, 对象的大小就是对应的成员变量的大小, 而成员函数不会占用对象的空间, 今天我们来讨论一下, 当类中定义了虚函数的时候, 此时对象的大小以及对象模型 非继承下的对象模型 class Base { public:virtual void func1(){cout << &qu…

软件测试相关概念

什么叫软件测试 软件测试就是测试产品没有错误,同时又证明软件是可以正确运行的 测试和调试的区别 调试一般都在开发期间 ,测试是伴随着整个软件的生命周期, 调试是发现程序中问题并且解决问题, 测试是发现程序中的缺陷 软件测试的目的和原则 目的:验证软件有没有问题 原…

C++静态成员函数访问非静态成员的几种方法

https://www.cnblogs.com/rickyk/p/4238380.html 大家都知道C中类的成员函数默认都提供了this指针&#xff0c;在非静态成员函数中当你调用函数的时候&#xff0c;编译器都会“自动”帮你把这个this指针加到函数形参里去。当然在C灵活性下面&#xff0c;类还具备了静态成员和静…

HDU2683——欧拉完全数

题目要求符合等式的数&#xff0c;我们首先要做的就是分析这个数&#xff1a; 对于这个等式&#xff0c;我们可能什么都看不出来&#xff0c;左边很难化简的样子&#xff0c;所以我们就要想到通过变化怎么样把右边化成和左边形式差不多的样子。结合组合数我们想到二项式定理&am…

获取网络接口信息——ioctl()函数与结构体struct ifreq、 struct ifconf

http://blog.csdn.net/windeal3203/article/details/39320605 Linux 下 可以使用ioctl()函数 以及 结构体 struct ifreq 结构体struct ifconf来获取网络接口的各种信息。 ioctl 首先看ioctl()用法ioctl()原型如下&#xff1a;#include <sys/ioctl.h>int ioctl(int fd, i…

java中引用传递

基本概念 栈内存 所谓的栈内存就是存储进程在运行过程中变量的内存空间 堆内存 所谓的堆内存就是存储系统中数据的内存空间 数组相关的引用传递 先来看一段代码 public class ArrayDemo {public static void main(String[] args) {int[] x null;x new int[3];System.o…

(原创)C++11改进我们的程序之右值引用

http://www.cnblogs.com/qicosmos/p/3369940.html 本次主要讲c11中的右值引用&#xff0c;后面还会讲到右值引用如何结合std::move优化我们的程序。 c11增加了一个新的类型&#xff0c;称作右值引用(R-value reference)&#xff0c;标记为T &&&#xff0c;说到右值引用…

(原创)C++11改进我们的程序之move和完美转发

http://www.cnblogs.com/qicosmos/p/3376241.html 本次要讲的是右值引用相关的几个函数&#xff1a;std::move, std::forward和成员的emplace_back&#xff0c;通过这些函数我们可以避免不必要的拷贝&#xff0c;提高程序性能。move是将对象的状态或者所有权从一个对象转移到另…

微型个人博客服务器

Http相关简介 Http是应用层的基于请求响应的一个协议, 其中Http的请求响应可以分为四部分. 请求行, 请求报头,空行, 请求正文.其中请求行包括了请求方法, url, 版本号, 请求报头包括请求属性, 冒分割的键值对, 每组属性之间都以换行的形式分开, 最后一空行作为请求的结束标识.…

[C/C++]关于C++11中的std::move和std::forward

http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.htmlstd::move是一个用于提示优化的函数&#xff0c;过去的c98中&#xff0c;由于无法将作为右值的临时变量从左值当中区别出来&#xff0c;所以程序运行时有大量临时变量白白的创建后又立刻销毁&#xff0c;其中又尤其是返回…

Linux I/O复用之select函数详解

http://blog.csdn.net/y396397735/article/details/55004775 select函数的功能和调用顺序 使用select函数时统一监视多个文件描述符的&#xff1a; 1、 是否存在套接字接收数据&#xff1f; 2、 无需阻塞传输数据的套接字有哪些? 3、 哪些套接字发生了异常&#xff1f; sel…

深入研究socket编程(3)——使用select函数编写客户端和服务器

http://blog.csdn.net/chenxun_2010/article/details/50488394 首先看原先《UNIX网络编程——并发服务器&#xff08;TCP&#xff09;》的代码&#xff0c;服务器代码serv.c&#xff1a; [cpp] view plaincopy #include<stdio.h> #include<sys/types.h> #inclu…

Ubuntu安装搭建Clion环境

呜呜呜&#xff0c;太辛苦了&#xff0c;我终于安装好这个了。 大概过程就是先在官网下载安装包&#xff0c;然后解压以后用终端移动到对应文件夹下运行clin.sh 运行完以后会有一些窗口&#xff0c;第一个选择don’t~~&#xff0c;然后点击ok 接受&#xff08;你可以不接受…

UNIX网络编程——select函数的并发限制和 poll 函数应用举例

http://blog.csdn.net/chenxun_2010/article/details/50489577 一、用select实现的并发服务器&#xff0c;能达到的并发数&#xff0c;受两方面限制 1、一个进程能打开的最大文件描述符限制。这可以通过调整内核参数。可以通过ulimit -n来调整或者使用setrlimit函数设置&#x…