生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用

全局初始化互斥量和条件变量(不全局也行,但至少要对线程启动函数可见,这样才能使用。)

static pthread_cont_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

使用互斥量锁住一块代码方法如下(默认忽略pthread开头的函数的错误检查,即类似 int s = pthread_xxx(...); if (s != 0) { printErrorMsg(s, ...); }这种代码)

pthread_mutex_lock(&mtx);
// TODO: 在这里添加自定义代码(被锁住的代码)
pthread_mutex_unlock(&mtx);

被锁住的代码是同步执行的,这里定义代码的执行时间段为[start, end],即一段代码开始执行的时刻为start,结束执行的时刻为end。

则任意两个线程执行lock和unlock之间的代码的时间段均不会重叠,也就是多线程执行被锁住的代码可以看做串行的,即总会有下述时间顺序:

A线程lock成功 >>> A线程执行被锁住的代码 >>> A线程unlock >>> B线程lock成功 >>> B线程执行被锁住的代码 >>> B线程unlock

条件变量的使用则是和互斥量挂钩的,若被锁住的代码存在等待操作,又不能确定等多久,此时就需要使用条件变量来等待。

举个生产者-消费者的例子,线程A为狗的活动,线程B为主人的活动。狗进食和主人添加狗粮2个行为是互斥的,即不能同时发生,必须有先后顺序,代码类似下面

void* consumer(void* arg)
{
    while (1) {  // 狗持续进食
        pthread_mutex_lock(&mtx);
        // TODO: 狗进食的代码
        pthread_mutex_unlock(&mtx);
    }
}

void* producer(void* arg)  // 主人的一次喂食
{
    pthread_mutex_lock(&mtx);
    // TODO: 主人喂食的代码
    pthread_mutex_unlock(&mtx);
}

而逻辑是,狗进食必须判断狗粮是否有剩余,也就是说狗进食的代码类似这样

// 狗进食的代码
if (hasFood())
    dog_eat();

这段代码被线程启动函数consumer的lock和unlock锁住,于是每次都要上锁 >>> 检查是否有食物 >>> 解锁,若主人长时间不喂食,狗的线程将会浪费大量系统资源在这几个不必要的步骤上面,因此狗的正确做法是在没有食物时等待,然后主人添加食物时通知狗去吃。类似进程通信的signal()和kill()函数。

等待过程在系统中相当于是阻塞,被阻塞的线程不会耗费系统资源。

而条件变量则是如此使用的,这种思路下的代码如下

void* consumer(void* arg)
{
    while (1) {  // 狗持续进食
        pthread_mutex_lock(&mtx);
        if (!hasFood())
            pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒

        dog_eat(); // 被唤醒则代表有食物了,可以吃了
        pthread_mutex_unlock(&mtx);
    }
}

void* producer(void* arg)  // 主人的一次喂食
{
    pthread_mutex_lock(&mtx);
    addFood();
    pthread_mutex_unlock(&mtx);

    pthread_cond_signal(&cond); // 添加食物后发出信号,唤醒被阻塞的consumer函数
}

消费者线程执行pthread_cond_wait时,会暂时unlock(),这样其他线程(生产者)才能继续执行。

生产者线程执行pthread_cond_signal时,会唤醒至少一条因为pthread_cond_wait而阻塞的线程,假如被唤醒的是执行consumer()函数的线程,pthread_cond_wait会返回,然后线程重新lock()。

——于是,其实这里的代码是有问题的,假如有一个线程先被唤醒,并吃空了狗粮,再之后,另一个线程才被唤醒,dog_eat()就会失败,因为狗粮被吃完了。

因此被唤醒时需要再检查一次

        if (!hasFood())
            pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒

        if (hasFood())
            dog_eat(); // 被唤醒则代表有食物了,可以吃了

但是如果唤醒后hasFood()返回“假”时,我们没有处理而是继续执行,而正常逻辑是,没有食物的时候还是要等待,然后再被唤醒,如果被唤醒时还是没食物继续等待,所以正确的写法是用while循环

        while (!hasFood())
            pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒

        dog_eat(); // 被唤醒则代表有食物了,可以吃了
时间: 2024-08-25 07:00:41

生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用的相关文章

使用互斥量和条件变量实现线程同步控制

管程(monitor)说明 在并发编程中,管程(monitor)是一个同步构件,管程实现了同一时间点,最多只有一个线程可以执行管程的某个子程序.与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程的实现很大程度上简化了程序设计. 管程可以确保一次只有一个进程执行管程中的程序,因此程序员不需要显式地编写同步代码,但是如果需要就某些特定条件上的同步,则需要定义一些条件结构(condition variable)来实现,并且对条件变量的操作仅有wait()和signal(),如下: condit

浅析线程间通信一:互斥量和条件变量

线程同步的目的简单来讲就是保证数据的一致性.在Linux中,常用的线程同步方法有互斥量( mutex ).读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题.另外还有自旋锁.barrier和信号量线程同步方法.本文将讨论互斥量和条件变量的使用,并给出了相

线程的互斥锁和条件变量通信机制

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <pthread.h> 5 6 #define BUFFER_SIZE 2 7 struct prodcons 8 { 9 int buffer[BUFFER_SIZE]; 10 pthread_mutex_t lock; 11 int readpos,writepos; 12 pthread_cond_t no

互斥量和条件变量

1.如何利用2个条件变量实现线程同步? 思路:就是来回的利用pthread_cond_signal()函数,当一方被阻塞时,唤醒函数可以唤醒pthread_cond_wait()函数,只不过pthread_cond_wait()这个方法要执行其后的语句,必须遇到下一个阻塞(也就是pthread_cond_wait()方法时),才执行唤醒后的其后语句. 代码如下: #include<stdio.h> #include<unistd.h> #include<stdlib.h>

读写锁————用互斥量和条件变量模拟

一. 读写锁 在多线程环境下为了防止对临界资源访问的冲突我们往往会在线程函数中加入互斥锁来完成线程间的互斥:但是,在有些情况下,互斥锁mutex并不是那么高效,比如当要对一块缓冲区进行读写操作的时候,因为读的需要比写入修改的需要要多,读取数据并不会修改缓冲区的数据个数或者内容,如果要使用互斥锁就会耗费额外的时间,每一次读取都要争夺锁资源挂起等待,因此就可以使用另外一种锁机制----读写锁. 有读写锁的存在当然就会有读者和写者,也就是多个线程,但是它们之间的相互关系和mutex锁中有所不同: 当读

互斥量和条件变量的区别

互斥量与条件变量的区别 转载自:http://www.360doc.com/content/12/0129/10/1317564_182456205.shtml 前面谈过了线程锁,下面我们在继续研究一下线程锁: 互斥量从本质上来说是一个锁,对互斥量加锁后任何其他试图给它加锁的线程都会被阻塞直至当前线程释放互斥量. 同样在设计时需要规定所有的线程必须遵守相同的数据访问规则,只有这样互斥机制才能正常工作(只要是锁都这样,这是锁工作的本质) 互斥量用pthread_mutex_t 数据类型表示,在使用

[转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http://blog.csdn.net/locape/article/details/6040383 http://www.cnblogs.com/liuweijian/archive/2009/12/30/1635888.html 一.什么是多线程? 当我自己提出这个问题的时候,我还是很老实的拿着操作系

POSIX 使用互斥量和条件变量实现生产者/消费者问题

boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t 和pthread_cond_t的一系列的封装.因此通过对原生态的POSIX 的mutex,cond的生成者,消费者的实现,我们可以再次体会boost带给我们的便利. 1. 什么是互斥量 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁.对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线 程将会被阻塞直到当前线

POSIX线程中互斥量的基本用法举例

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <semaphore.h> void *thread_function( void *arg ); pthread_mutex_t work_mutex; #define WORK_SIZE 1024 char work