各位上过《操作系统》的同学都知道进程同步这个知识点,汤小丹那本“考研指定教材”上面描述 PV 操作是用 Pascal 语言的,看着真心难受,各种题目也是要求我们纸上写程序,本人真心反感,写了都不知道对不对。下面这个程序是在 Windows 下用 C 语言写的,选的例子是经典的“生产者消费者问题”,希望对大家有所帮助。
“生产者消费者问题”的程序如下,假设有多个缓冲区,多个生产者,多个消费者。它们的数量可以在宏定义中设置。
/********************************************************************
created: 2013/11/06
created: 16:54
file base: producer_consumer
file ext: c
author: Justme0 (http://blog.csdn.net/Justme0)
purpose: 生产者消费者问题
*********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <time.h>
#define WAIT_TIME 100 // 每次输出后暂停,便于观察
#define BUFFER_SIZE 2 // 缓冲区数目
#define PRODUCER_NUM 1 // 生产者数目
#define CONSUMER_NUM 1 // 消费者数目
typedef int Item; // 产品类型
Item productID = 1; // 产品ID
Item NOTHING_TAG = 0; // 表明该处为空缓冲区(用于输出显示)
Item buffer[BUFFER_SIZE]; // 缓冲区(所有元素应初始化为 NOTHING_TAG)
int in = 0; // 生产者指针(右开)
int out = 0; // 消费者指针(左闭)
HANDLE empty; // 消费者--生产者同步信号量
HANDLE full; // 生产者--消费者同步信号量
HANDLE mutex; // 互斥信号量(若给生产者、消费者分别设置互斥量,则并发度更高)
void put_item(Item item);
Item get_item();
Item produce_item();
void consume_item(Item item);
DWORD WINAPI producer(LPVOID param);
DWORD WINAPI consumer(LPVOID param);
void print_buffer();
void print_init();
/*
** 生产者进程
*/
DWORD WINAPI producer(LPVOID param) {
Item item;
while(TRUE) {
item = produce_item();
WaitForSingleObject(empty, INFINITE);
WaitForSingleObject(mutex, INFINITE);
put_item(item);
ReleaseMutex(mutex);
ReleaseSemaphore(full, 1, NULL);
}
return 0;
}
/*
** 消费者进程
*/
DWORD WINAPI consumer(LPVOID param) {
Item item;
while(TRUE) {
WaitForSingleObject(full, INFINITE);
WaitForSingleObject(mutex, INFINITE);
item = get_item();
ReleaseMutex(mutex);
ReleaseSemaphore(empty, 1, NULL);
consume_item(item);
}
return 0;
}
int main(int argc, char **argv) {
int i;
freopen("cout.txt", "w", stdout);
// 创建各信号量
empty = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);
full = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);
mutex = CreateMutex(NULL, FALSE, NULL);
// 输出初始状态
print_init();
// 创建生产者线程
for (i = 0; i != PRODUCER_NUM; ++i) {
_beginthreadex(NULL, 0, producer, NULL, 0, NULL);
}
// 创建消费者进程
for (i = 0; i != CONSUMER_NUM; ++i) {
_beginthreadex(NULL, 0, consumer, NULL, 0, NULL);
}
while(TRUE);
return 0;
}
void put_item(Item item) {
Sleep(WAIT_TIME);
printf("\n生产者投入%d\n", item);
buffer[in] = item;
in = (1 + in) % BUFFER_SIZE;
print_buffer();
}
Item get_item() {
Item item = buffer[out];
Sleep(WAIT_TIME);
buffer[out] = NOTHING_TAG;
out = (1 + out) % BUFFER_SIZE;
printf("\n消费者获取%d\n", item);
print_buffer();
return item;
}
Item produce_item() {
Item item = productID++;
printf("生产者生产出%d\n", item);
return item;
}
void consume_item(Item item) {
printf("消费者消费掉%d\n", item);
}
void print_buffer() {
int i;
for (i = 0; i != BUFFER_SIZE; ++i) {
printf("buffer[%d]:", i);
if (buffer[i] != NOTHING_TAG) {
printf(" %d", buffer[i]);
}
if (i == out) {
printf(" <-- 消费者");
}
if (i == in) {
printf(" <-- 生产者");
}
printf("\n");
}
printf("\n");
}
void print_init() {
printf("初始状态:\n");
printf("生产者个数:%d\n", PRODUCER_NUM);
printf("消费者个数:%d\n", CONSUMER_NUM);
printf("缓冲区个数:%d\n", BUFFER_SIZE);
printf("初始缓冲区状态:\n");
print_buffer();
}
某次运行的结果如下
初始状态:
生产者个数:1
消费者个数:1
缓冲区个数:2
初始缓冲区状态:
buffer[0]: <-- 消费者 <-- 生产者
buffer[1]:
生产者生产出1
生产者投入1
buffer[0]: 1 <-- 消费者
buffer[1]: <-- 生产者
生产者生产出2
消费者获取1
buffer[0]:
buffer[1]: <-- 消费者 <-- 生产者
消费者消费掉1
生产者投入2
buffer[0]: <-- 生产者
buffer[1]: 2 <-- 消费者
生产者生产出3
消费者获取2
buffer[0]: <-- 消费者 <-- 生产者
buffer[1]:
消费者消费掉2
生产者投入3
buffer[0]: 3 <-- 消费者
buffer[1]: <-- 生产者
生产者生产出4
消费者获取3
buffer[0]:
buffer[1]: <-- 消费者 <-- 生产者
消费者消费掉3
生产者投入4
buffer[0]: <-- 生产者
buffer[1]: 4 <-- 消费者
生产者生产出5
消费者获取4
buffer[0]: <-- 消费者 <-- 生产者
buffer[1]:
消费者消费掉4
生产者投入5
buffer[0]: 5 <-- 消费者
buffer[1]: <-- 生产者
这是我第一次接触多线程程序,上次去面试被问到多线程完全空白,桑心啊。