本文来自个人博客:https://dunkwan.cn
文章目录
Classic Problem of Synchronization(经典同步问题)
在这个部分,我们要讲述大量同步问题,像一大类并发控制问题的例子。对于测试几乎每一个最新提出的同步主题,这些问题都有被用到。在我们的这类问题的解决方案中,因为同步信号量是一种解决该问题的传统方式,所以我们使用同步信号量。然而,这些解决方案的具体实现可能在二元信号量的地方使用互斥锁。
while (true){
...
/* produce an item an next_produced */
...
wait(empty);
wait(mutex);
...
/* add next_produced to the buffer */
...
signal(mutex);
signal(full);
}
The Bounded-Buffer Problem(有限缓冲问题)
该问题被用于说明同步原语的能力。这里,我们仅提出这个话题的普遍结构而不给出具体的实现。
在我们的问题中,生产者和消费者进程共享了如下数据结构:
int n;
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0;
我们假设缓冲池由n个缓冲项组成,每一个能够存一个数据项。
mutex
信号量提供了对于缓冲区的互斥能力,并且被初始化为1。empty
和full
信号量表示空缓冲和满缓冲的数量。empty
被初始化为n,full
被初始化为0。 如上是生产者进程的代码,下面则是消费者的代码。注意两者的对称性。我们可以将代码解释为生产者为消费者生产满缓冲项,而消费者则为生产者生产空缓冲项。
while(true){
wait(full);
wait(mutex);
...
/* remove an item from buffer to next_consumed */
...
signal(mutex);
signal(empty);
...
/* consume the item in next_consumed */
...
}
The Producer-Consumer Problem(生产者消费者问题)
在带有有限缓冲的生产者消费者问题中,我们提出了基于信号量的解决方案。由上面可知,对于生产者消费者问题,我们使用了三个信号量
empty
、full
和mutex
。empty
和full
分别代表缓冲区中满缓冲和空缓冲的项数,而mutex
则是二元信号量(或互斥)。对于该问题而言,应当将mutex
称为互斥锁,而非二元信号量。
The Buffer
buffer
代表一个元素类型为buffer_item
固定大小的数组。这个数组被实现为一个循环队列。以下就是其在头文件中的定义。
/* buffer.h */
typedef int buffer_item;
#define BUFFER_SIZE 5
同时该缓冲区还被两个操作函数控制,分别是
insert_item
和remove_item
函数。它们被生产者和消费者线程所调用。以下是它们的实现框架。
#include "buffer.h"
/* buffer */
buffer_item buffer[BUFFER_SIZE];
int insert_item(buffer_item item){
/* insert item into buffer
return 0 if successful, otherwise
return -1 indicating an error condition */
}
int remove_item(buffer_item *item){
/* remove an object from buffer
placing it in item
return 0 if successful, otherwise
return -1 indicating an error condition */
}
insert_item
和remove_item
函数将会同步上面所列的生产者和消费的模型。缓冲区也需实现初始化互斥对象mutex
,empty
和full
信号量。
main
函数将负责初始化缓冲区和创建生产者消费者线程。主函数中将会通过命令行传递以下参数。
- 终止前的睡眠时间
- 生产者线程的数量
- 消费者线程的数量
#include "buffer.h"
int main(int argc, char *argv[]){
/* 1. Get command line arguments argv[1], argv[2], argv[3] */
/* 2. Initialize buffer */
/* 3. Create producer thread(s) */
/* 4. Create consumer thread(s) */
/* 5. Sleep */
/* 6. Exit */
}
The Producer and Consumer Threads
生产者要么将睡眠一个随机的时间,要么将插入一个随机数到缓冲区中。随机数将由
rand
函数来产生,该函数将会生成一个大小在0 ~ RAND_MAX
的随机数。消费者将睡眠随机时间,在它被唤醒后,将尝试去移除缓冲区中的一项。生产和消费者线程框架如下。
#include <stdlib.h> /* required for rand() */
#include "buffer.h"
void *producer(void *param){
buffer_item item;
while(true){
/* sleep for random period of time */
sleep(...);
/* generate a random number */
item = rand();
if(insert_item(item))
fprintf("report error condition\n");
else
printf("producer produced %d\n", item);
}
}
void *consumer(void *param){
buffer_item item;
while(true){
/* sleep for a random period of time */
sleep(...);
if(remove_item(&item))
fprintf("report error condition");
else
printf("consumer consumed %d\n", itme);
}
}
Pthreads Thread Creation and Synchronization
下面将利用POSIX线程库以及信号量来进行线程创建和同步操作。
以下是具体对于生产者消费者模型的实现。
/* buffer.h */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <stdbool.h>
typedef int buffer_item;
#define BUFFER_SIZE 5
buffer_item buffer[BUFFER_SIZE];
sem_t full, empty;
pthread_mutex_t mutex;
int insert_item(int);
int remove_item(int *);
void *producer(void *);
void *consumer(void *);
/* buffer.c */
#include "buffer.h"
int in = 0;
int out = 0;
int count = 0;
int insert_item(buffer_item item)
{
if(count == BUFFER_SIZE)
return -1;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
count++;
return 0;
}
int remove_item(buffer_item *item)
{
if(count == 0)
return -1;
*item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
return 0;
}
void *producer(void *param)
{
buffer_item item;
while(true){
sleep(1);
item = random()%100 + 1;
sem_wait(&empty);
pthread_mutex_lock(&mutex);
if(insert_item(item))
printf("report error condition for insert\n");
else
printf("producer produced %d\n", item);
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
void *consumer(void *param)
{
buffer_item item;
while(true){
sleep(1);
sem_wait(&full);
pthread_mutex_lock(&mutex);
if(remove_item(&item))
printf("report error condition for remove\n");
else
printf("consumer consumed %d\n", item);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
/* main.c */
#include "buffer.h"
int main(int argc, char *argv[])
{
if(argc > 4)
{
perror("args error");
exit(1);
}
memset(buffer, 0, sizeof(buffer));
pthread_t prod[atoi(argv[2])], cons[atoi(argv[3])];
sem_init(&empty, 0, 5);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
int i;
for(i = 0; i < atoi(argv[2]); i++)
pthread_create(&prod[i], NULL, producer, NULL);
for(i = 0; i < atoi(argv[3]); i++)
pthread_create(&cons[i], NULL, consumer, NULL);
sleep(atoi(argv[1]));
for(i = 0; i < atoi(argv[2]); i++)
pthread_join(prod[i], NULL);
for(i = 0; i < atoi(argv[3]); i++)
pthread_join(cons[i], NULL);
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
exit(0);
}
/* Makefile */
EXE=producer-consumer
CC=gcc
LIBS=-pthread
SRCS= buffer.c main.c
all : $(EXE)
producer-consumer : $(SRCS)
$(CC) -o $@ $^ $(LIBS)
clean :
rm -rf $(EXE)
结果如下:
源代码
本文大部分内容来自操作系统概念中关于进程同步的相关内容。
来源:CSDN
作者:Dunk.Wan
链接:https://blog.csdn.net/qq_40073459/article/details/104752813