一、实验描述
利用有限缓冲问题的解决思想,实现生产者消费者问题,主要利用线程同步机制完成。使用三个信号量: empty (以记录有多少空位)、full (以记录有多少满位)以及mutex (二进制信号量或互斥信号量,以保护对缓冲插入与删除的操作)。对于本项目, empty 与full 将采用标准计数信号量,而mutex 将采用二进制信号量。生产者与消费者作为独立线程,在empty、full、mutex 的同步前提下,对缓冲进行插入与删除。
二、实验测试样例
测试数据文件包括n行测试数据,分别描述创建的n个线程是生产者还是消费者,以及生产者或消费者存放或取产品的开始时间和持续时间。每行测试数据包括四个字段,各个字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,P表示生产者,C表示消费者。第三字段为一个正数,表示存放或取出操作的开始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的使用申请。第四字段为一个正数,表示操作的持续时间。第五字段为一个正数(仅生产者有),表示生产的产品号。当线程申请成功后,开始对共享资源的操作,该操作持续相应时间后结束,并释放共享资源。例如:
1 C 3 5
2 P 4 5 1
3 C 5 2
4 C 6 5
5 P 7 3 2
6 P 8 4 3
三、知识准备
Linux POSIX线程互斥和同步相关知识。
|:mutex初始化
线程的互斥量mutex数据类型是pthread_mutex_t。 使用前要对它进行初始化,有两种方法:
(1)静态分配的互斥量的初始化:
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
(2)动态分配的互斥量的初始化:
首先申请内存(malloc),
再用pthread_mutex_init进行初始化。
释放时,必须先调用pthread_mutex_destroy,而后再释放内存(free)。
例如:
//创建10个线程计算0~9的和。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#define THREAD_NUMBER 10
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sum =0;
void* inc(void *arg) //线程执行的函数
{
int i =(*(int *)arg);
pthread_mutex_lock(&mutex);
sum = sum +i;
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t pt[THREAD_NUMBER];
int i;
int arg[THREAD_NUMBER];
for(i=0; i<THREAD_NUMBER; i++) {
arg[i]=i;
if(pthread_create(&pt[i], NULL, inc, (void *)&arg[i])!=0){
printf("pthread_create error\n");
exit(1);
}
}
for(i=0; i<THREAD_NUMBER; i++)
if(pthread_join(pt[i],NULL)!=0){
printf("pthread_join error\n");
exit(1);
}
printf("sum is %d\n",sum);
pthread_mutex_destroy(&mutex);
return 0;
}
|:POSIX 信号量
POSIX 信号量在多线程编程中可以起到同步或互斥的作用。用POSIX 信号量可以实现传统操作系统P、V操作(即对应课本的wait、signal)。
由于POSIX信号量不是内核负责维护,所以当进程退出后,POSIX信号量自动消亡。
#include <semaphore.h>
//定义信号量
sem_t sem;
//创建信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
//相当于wait操作,即申请资源。
int sem_wait(sem_t * sem);
//是函数sem_wait()的非阻塞版本。若信号量大于0,它直接将信号量sem的值减一并返回0;否则,它立即返回错误类型EAGAIN。
int sem_trywait(sem_t * sem);
//相当于signal操作,释放资源
int sem_post(sem_t * sem);
//获得信号量当前值
int sem_getvalue(sem_t * sem, int * sval);
//用来释放信号量
int sem_destroy(sem_t * sem);
四、代码实现
buffer.h
#ifndef BUFFER_H_
#define BUFFER_H_
#include <iostream>
#define BUFFER_SIZE 5
using namespace std;
typedef int buffer_item;
struct Buffer{
Buffer(){
count = 0;
front = 0;
rear = 0;
for(int i = 0; i < BUFFER_SIZE; i++){
items[i] = 0;
}
}
buffer_item items[BUFFER_SIZE];
int count;
int front;
int rear;
};
int insert_item(buffer_item item);
int remove_item();
#endif
buffer.cpp
#include "buffer.h"
Buffer *buffer = new Buffer();
int insert_item(buffer_item item){
if(buffer->count < BUFFER_SIZE){
buffer->items[buffer->rear] = item;
cout << "Product: " << buffer->items[buffer->rear] << endl;
buffer->count++;
buffer->rear++;
if(buffer->rear % BUFFER_SIZE == 0) buffer->rear = 0;
return 0;
}
return -1;
}
int remove_item(){
if(buffer->count > 0){
cout<< "Consume: " << buffer->items[buffer->front] << endl;
buffer->items[buffer->front] = 0;
buffer->count--;
buffer->front++;
if(buffer->front % BUFFER_SIZE == 0) buffer->front = 0;
return 0;
}
return -1;
}
deal.h
#ifndef DEAL_H_
#define DEAL_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdlib>
#include <iostream>
using namespace std;
typedef struct Data{
Data(){
serial_num = 0;
character = ' ';
start = 0;
last = 0;
product = 0;
}
int serial_num;
char character;
int start;
int last;
int product;
} Instruction;
Instruction *Deal(char file[]);
#endif
deal.cpp
#include "deal.h"
Instruction *Deal(char file[]){
Instruction *ins = new Data();
int i;
int j = 0;
string tmp = "";
int len = strlen(file);
for(i = 0; i < len; i++){
if(file[i] != ' ' && file[i] != '\n')
tmp += file[i];
else{
if(tmp != ""){
switch(j){
case 0: ins->serial_num = atoi(tmp.c_str());
tmp = "";
break;
case 1: ins->character = tmp.at(0);
tmp = "";
break;
case 2: ins->start = atoi(tmp.c_str());
tmp = "";
break;
case 3: ins->last = atoi(tmp.c_str());
tmp = "";
break;
case 4: ins->product = atoi(tmp.c_str());
tmp = "";
break;
}
j++;
}
}
}
return ins;
}
Main.cpp
#include <pthread.h>
#include <sys/types.h>
#include <semaphore.h>
#include <unistd.h>
#include "buffer.h"
#include "deal.h"
#define MAX 20
struct sto{
sem_t mutex;
sem_t nempty;
sem_t nstored;
};
struct sto shared;
void *produce(void *arg)
{
Instruction *ins = (Instruction*)arg;
sem_wait(&shared.nempty);
sem_wait(&shared.mutex);
sleep(ins->start);
insert_item(ins->product);
sleep(ins->last);
sem_post(&shared.mutex);
sem_post(&shared.nstored);
return(NULL);
}
void *consume(void *arg)
{
Instruction *ins = (Instruction*)arg;
sem_wait(&shared.nstored);
sem_wait(&shared.mutex);
sleep(ins->start);
remove_item();
sleep(ins->last);
sem_post(&shared.mutex);
sem_post(&shared.nempty);
return(NULL);
}
int main()
{
FILE *fp;
char file[MAX];
int i;
pthread_t tid_produce;
pthread_t tid_consumer;
Instruction *ins = new Data();
sem_init(&shared.mutex, 0, 1);
sem_init(&shared.nempty, 0, 5);
sem_init(&shared.nstored, 0, 0);
fp = fopen("test.txt", "r");
while(fgets(file, MAX, fp)){
ins = Deal(file);
if(ins->character == 'P'){
pthread_create(&tid_produce, NULL, produce, (void*)ins);
}
else{
pthread_create(&tid_consumer, NULL, consume, (void*)ins);
}
/*cout<<ins->serial_num<<" "<<ins->character<<" ";
cout<<ins->start<<" "<<ins->last<<" ";
cout<<ins->product<<endl;*/
}
sem_destroy(&shared.mutex);
sem_destroy(&shared.nempty);
sem_destroy(&shared.nstored);
sleep(60);
return 0;
}
Makefile
CC = g++
objects = Main.o buffer.o deal.o
Main: $(objects)
$(CC) -o Main $(objects) -pthread
Main.o: Main.cpp
buffer.o: buffer.h
deal.o: deal.h
.PHONY: clean
clean:
-rm Main $(objects)