Linux多线程机制(生产者和消费者实例 )

    使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立 的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相 同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要 的时间。

  使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不 仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数 据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是 编写多线程程序时最需要注意的地方。

1、函数语法简述。

线程的创建和终止

pthread_create

头文件:       pthread.h

函数原型:    int pthread_create (pthread_t* thread, pthread_attr_t* attr,

void* (start_routine)(void*), void* arg);

函数传入值: thread: 线程标识符

attr: 线程属性设置

start_routine:线程函数入口

arg:传递给线程函数的参数

返回值:       0: 成功

-1: 失败

    调用此函数可以创建一个新的线程,新线程创建后执行start_routine 指定的程序。其中参数attr是用户希望创建线程的属性,当为NULL时表示以默认的属性创建线程。arg是向start_routine 传递的参数。当成功创建一个新的线程时,系统会自动为新线程分配一个线程ID号,并通过pthread 返回给调用者。

pthread_exit

头文件:      pthread.h

函数原型:   void pthread_exit(void *value_ptr);

函数传入值:retval:pthread_exit()调用者线程的返回值,可又其他函数如pthread_join来检索获取。

调用该函数可以退出线程,参数value_ptr是一个指向返回状态值的指针。

phread_join

头文件:      pthread.h

函数原型:   int pthread_join (pthread_t* thread, void** thread_return);

函数传入值:thread:等待线程的标识符。

thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL值);

函数返回值:成功: 0

失败:-1

这个函数的作用是等待一个线程的结束。调用pthread_join()的线程将被挂起直到线程ID为参数thread 指定的线程终止。

pthread_self(void);

为了区分线程,在线程创建时系统为其分配一个唯一的ID号,由pthread_create()返回给调用者,也可以通过pthread_self()获取自己的线程ID。

pthread_detach

  创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)

  但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码

  pthread_detach(pthread_self())

  或者父线程调用

  pthread_detach(thread_id)(非阻塞,可立即返回)

  这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。

linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,

如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。

若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。

unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.

pthread_mutex_init

头文件:                  <pthread.h>

函数原型:               int pthread_mutex_init (pthread_mutex_t* mutex,

const pthread_mutexattr_t* mutexattr);

函数传入值:            mutex:互斥锁。

mutexattr:PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁。

PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁。

PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁。

函数返回值:            成功:0

出错:-1

上锁函数:

int pthread_mutex_lock(pthread_mutex_t* mutex);

int pthread_mutex_trylock (pthread_mutex_t* mutex);

int pthread_mutex_unlock (pthread_mutex_t* mutex);

int pthread_mutex_destroy (pthread_mutex_t* mutex);

函数传入值:            mutex:互斥锁。

函数返回值:            同上。

pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);初使化一个互斥体变量mutex,参数attr表示按照attr属性创建互斥体变量mutex,如果参数attr为NULL,则以默认的方式创建。

pthread_mutex_lock(pthread_mutex_t *mutex);给一个互斥体变量上锁,如果mutex指定的互斥体已经被锁住,则调用线程将被阻塞直到拥有mutex的线程对mutex解锁为止。

Pthread_mutex_unlock(pthread_mutex_t *mutex);对参数mutex指定的互斥体变量解锁。

线程间的同步

同步就是线程等待某一个事件的发生,当等待的事件发生时,被等待的线程和事件一起继续执行。如果等待的事件未到达则挂起。在linux操作系统中是通过条件变量来实现同步的。

Pthread_cond_init(pthread_cond_t *cond,const pthread_cond_t *attr);这个函数按参数attr指定的属性初使化一个条件变量cond。

Pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);等待一个事件(条件变量)的发生,发出调用的线程自动阻塞,直到相应的条件变量被置1。等待状态的线程不占用CPU时间。

pthread_cond_signal(pthread_cond_t *cond);解除一个等待参数cond指定的条件变量的线程的阻塞状态。

    

   
生产者和消费者问题,生产者线程向一缓冲区中写数据,消费者线程从缓冲区中读取数据,由于生产者线程和消费者线程共享同一缓冲区,为了正确读写数据,在使用缓冲队列时必须保持互斥。生产者线程和消费者线程必须满足:生产者写入缓冲区的数目不能超过缓冲区容量,消费者读取的数目不能超过生产者写入的数目。在程序中使用了一个小技巧来判断缓冲区是空还是满。在初始化时读指针和写指针为0;如果读指针等于写指针,则缓冲区是空的;如果(写指针+ 1) % N 等于读指针,则缓冲区是满的,%表示取余数,这时实际上有一个单元空出未用。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<pthread.h>
#define BUFFER_SIZE 8 

struct prodcons {
    int buffer[BUFFER_SIZE];
    pthread_mutex_t lock;      //互斥LOCK
    int readpos , writepos;
    pthread_cond_t notempty;   //缓冲区非空条件判断
    pthread_cond_t notfull;    //缓冲区未满条件判断
};

void init(struct prodcons * b){
    pthread_mutex_init(&b->lock,NULL);
    pthread_cond_init(&b->notempty,NULL);
    pthread_cond_init(&b->notfull,NULL);
    b->readpos=0;
    b->writepos=0;
}

void put(struct prodcons* b,int data){
    pthread-_mutex_lock(&b->lock);
    if((b->writepos + 1) % BUFFER_SIZE == b->readpos)
    {
        pthread_cond_wait(&b->notfull, &b->lock) ;
    }
    b->buffer[b->writepos]=data;
    b->writepos++;
    if(b->writepos >= BUFFER_SIZE)
        b->writepos=0;
    pthread_cond_signal(&b->notempty);
    pthread_mutex_unlock(&b->lock);
}
int get(struct prodcons *b){
    int data;
    pthread_mutex_lock(&b->lock);
    if(b->writepos == b->readpos)
    {
        pthread_cond _wait(&b->notempty, &b->lock);
    }
    data = b->buffer[b->readpos];
    b->readpos++;

    if(b->readpos >= BUFFER_SIZE)
        b->readpos=0;
    pthread_cond_signal(&b->notfull);
    pthread_mutex_unlock(&b->lock);
    return data;
}

#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
    int n;

    for(n = 0; n < 10000; n++)
    {
        printf("%d \n", n) ;
        put(&buffer, n);
    }
    put(&buffer, OVER);
    return NULL;
}
void *consumer(void * data)
{
    int d;

    while(1)
    {
        d = get(&buffer);
        if(d == OVER)
            break;
        printf("%d\n", d);
    }
    return NULL;
}

int main(void)
{
    pthread_t th_a, th_b;
    void *retval;
    init(&buffer);
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);
    pthread_join(th_a, &retval);
    pthread_join(th_b, &retval);
    return 0;
}

生产者负责将1到1000的整数写入缓冲区,而消费者负责从同一个缓冲区中读取写入的整数并打印出来。因为生产者和消费者是两个同时运行的线程,并且要使用同一个缓冲区进行数据交换,因此必须利用一种机制进行同步。

    多线程的最大好处是,除堆栈之外,几乎所有的数据均是共享的,因此线程间的通讯效率很高;

    缺点:因为共享所有数据,从而非常容易导致线程之间互相破坏数据。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-30 10:19:19

Linux多线程机制(生产者和消费者实例 )的相关文章

开启子进程的两种方式,孤儿进程与僵尸进程,守护进程,互斥锁,IPC机制,生产者与消费者模型

开启子进程的两种方式 # # # 方式一: # from multiprocessing import Process # import time # # def task(x): # print('%s is running' %x) # time.sleep(3) # print('%s is done' %x) # # if __name__ == '__main__': # # Process(target=task,kwargs={'x':'子进程'}) # p=Process(tar

java_多线程_生产者与消费者(并发协作)

对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一样,Hello World!都是最经典的例子. 实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了.对于此模型,应该明确一下几点:1.生产者仅仅在仓储未满时候生产,仓满则停止生产.2.消费者仅仅在仓储有产品时候才能消费,仓空则等待.3.当消费者发现仓储没产品可消费时候会通知生产者生产.4.生产者在生产出可消费产品时候,应该通知等待的消费者去消费. packa

java多线程实现生产者与消费者---经典问题

前几天老师领着学习了一下单线程和多线程的题目. 这里是操作系统中非常经典的题目,生产者和消费者的题,这里涉及的是仓库, 只有一个人(生产者或消费者)进入,另一个人只有等待. 这里的重点是关于传值的问题.一定要保持一致,不然,对于存和取 的对象,就可能出现多个. //========================================================================// 仓库类 //====================================

多线程--简单生产者与消费者(Lock锁)

前两篇的生产者与消费者(多线程)运用的是synchronized进行同步锁的,本次将运用JDK1.5提供的Lock锁. 它 将synchronized替换成了Lock将Object中的wait notify notifyAll替换成了Condition对象, Condition可以被Lock获取, 可以创建多个Condition对象,只唤醒对方操作. 具体代码如下: package cn.zz; import java.util.concurrent.locks.Condition;import

Java多线程的生产者与消费者模型,线程间的通信

java多线程中的生产者与消费者模式:首先有一个阻塞队列,生产者将生产的东西放到队列里,消费者再从队列中取.当队列中的东西数量达到其容量就发生阻塞. import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import jav

java 22 - 16 多线程之生产者和消费者的问题

生产者和消费者问题的描述图 通过上图,我们可以发现: 生产者和消费者使用的都是同一个资源(肉包子) 所以,当使用线程的时候,这两类的锁也是同一把锁(为了避免出现线程安全问题) 例子:学生信息的录入和获取 * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * 资源类:Student 1 public class Student { 2 3 String name; 4 int age; 5

Linux多线程模拟生产者/消费者问题

描述: 生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制.在同一个进程地址空间内执行的N个线程生产者线程生产物品,然后将物品放置在一个空缓冲区中供N个消费者线程消费.消费者线程从缓冲区中获得物品,然后释放缓冲区.当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区.当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来. 输入: 生产者个数.消费者个数.还是缓冲

Linux下用条件变量实现多线程间生产者与消费者问题

一. 线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变量(Condition Variable)来阻塞等待某个条件,或者唤醒等待这个条件的线程.Condition Variablepthread_cond_t类型的变量表,可以这样初始化和销毁: 返回值:成功返回0,失败返回错误号. 一个Condition Variable总是和一个Mutex搭配使

Java:多线程之生产者与消费者

要求:用两个线程模拟存票.售票过程.但要求每存入一张票,就售出一张票,售出后,再存入,直到售完为止. 用到的知识点:线程等待.唤醒.可能的线程中断异常 下面的方式一和方式二采用的是唤醒所有等待的线程,即wait()和notify()方法 方式一:继承Thread class Tickets //定义(资源)票类 { protected int size;//总票数 int number=0; //票号 Boolean available=false;//表示当前是否有票可售 public Tic