实验题目:多线程实现生产消费者模型(程序可在Linux下和VS2019中运行)
实验要求:1.生产者4个,消费者8个,仓库容量20个,初始为10个。
---------------2.生产者和消费者生产消费完毕后需要休眠一段时间。
---------------3.货物为随机产生的大写字母。
实验环境:1.windows11、visual studio 2019 X86
---------------2.Linux、ubuntu20、gcc7.4.0、secureCRT4.0、notepad++
参考内容: 无法打开pthread.h
注意:1. pthread的下载链接为:
https://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip
-------- 2.请注意 参考内容 “无法打开pthread.h” 内的2.3 dll文件内x86 下的文件复制到C:\Windows\SysWOW64
一、使用条件变量和互斥锁
1.原理
从上图中可以看出在不满足条件下,消费者线程会进入触发pthread_cond_wait(&g_cond,&g_mutex),然后该消费者线程被进入条件队列,并释放锁(该锁由条件变量释放);这时生产者抢到锁,生产,释放信号量,解锁;当条件队列当中的等待的消费者线程接收到该信号后,上锁(紧接着pthread_cond_wait()后进行,上锁由条件变量进行),消费,解锁。这样就完成了一个条件变量和互斥锁的相互使用。
2.程序
/*!
*************************************************************************************************
* @file main.cpp
* @brief producer consumer model Production consumption model
* @author Super Pallet-Town Man
* date 2022/9/10
*************************************************************************************************
*/
// Windows system
#include <windows.h>
//Linux use
//#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define CONSUMERS_NEMBER 4
#define PRODUCERS_NEMBER 2
#define MAX_BUDDER_SIZE 20
#define INIT_BUFFER_SIZE 10
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;
pthread_t g_threadConsumers[CONSUMERS_NEMBER];
pthread_t g_threadProducers[PRODUCERS_NEMBER];
typedef struct _ST_BUF
{
int iCount;
char strArray[MAX_BUDDER_SIZE + 1];
}ST_BUF;
static ST_BUF stBuf = { INIT_BUFFER_SIZE , {0}};
/*!
* @brief generate random letters
*
* @return letter
*/
char getRandLetter()
{
char letter;
letter = (char)((rand() % 26) + 'A');
return letter;
}
/*!
* @brief consumer model function
*
* @param[in] arg the serial number
*
* @return NULL to end
*/
void* consumer(void* arg)
{
if (NULL == arg)
{
exit(-1);
}
int num = *(int*)arg;
while (1)
{
pthread_mutex_lock(&g_mutex);
while (stBuf.iCount == 0)
{
pthread_cond_wait(&g_cond, &g_mutex);
}
stBuf.iCount--;
printf("[Consumer %d] consumer [%c] ", num, stBuf.strArray[stBuf.iCount]);
stBuf.strArray[stBuf.iCount] = 0;
printf("%s\n", stBuf.strArray);
pthread_cond_signal(&g_cond);
pthread_mutex_unlock(&g_mutex);
Sleep((rand() % 200) * 10);//in windows system, Sleep() is ms
//usleep((rand() % 200) * 1000);//Linux use
}
return NULL;
}
/*!
* @brief producer model function
*
* @param[in] arg the serial number
*
* @return NULL to end
*/
void* producer(void* arg)
{
if (NULL == arg)
{
exit(-1);
}
int num = *(int*)arg;
char ch = 0;
while (1)
{
pthread_mutex_lock(&g_mutex);
while (stBuf.iCount >= MAX_BUDDER_SIZE)
{
pthread_cond_wait(&g_cond, &g_mutex);
}
ch = getRandLetter();
printf("[Producer %d] produce [%c] ", num, ch);
stBuf.strArray[stBuf.iCount++] = ch;
printf("%s\n", stBuf.strArray);
pthread_cond_signal(&g_cond);
pthread_mutex_unlock(&g_mutex);
Sleep((rand() % 100) * 10);//in windows system, Sleep() is ms
//usleep((rand() % 100) * 1000);//Linux use
}
return NULL;
}
/*!
* @brief main function
*/
int main()
{
pthread_mutex_init(&g_mutex, NULL);
pthread_cond_init(&g_cond, NULL);
char strArrayInit[INIT_BUFFER_SIZE + 1] = { 0 };
for (int i = 0; i < INIT_BUFFER_SIZE; i++)
{
strArrayInit[i] = getRandLetter();
}
memcpy(stBuf.strArray, strArrayInit, INIT_BUFFER_SIZE);
for (int i = 0; i < PRODUCERS_NEMBER; i++)
{
pthread_create(&g_threadProducers[i], NULL, producer, (void*)&i);
Sleep(1);
//usleep(1000);//Linux use
}
for (int j = 0; j < CONSUMERS_NEMBER; j++)
{
pthread_create(&g_threadConsumers[j], NULL, consumer, (void*)&j);
Sleep(1);
//usleep(1000);//Linux use
}
for (int i = 0; i < CONSUMERS_NEMBER; i++)
{
pthread_join(g_threadConsumers[i], NULL);
}
for (int j = 0; j < PRODUCERS_NEMBER; j++)
{
pthread_join(g_threadProducers[j], NULL);
}
pthread_mutex_destroy(&g_mutex);
pthread_cond_destroy(&g_cond);
return 0;
}
二、只使用互斥锁的程序
这里只将列出了修改的代码(consumer 和 producer 的API),其余部分一致
/*!
* @brief consumer model function
*
* @param[in] arg the serial number
*
* @return NULL to end
*/
void* consumer(void* arg)
{
if (NULL == arg)
{
exit(-1);
}
int num = *(int*)arg;
while (1)
{
pthread_mutex_lock(&g_mutex);
if (stBuf.iCount > 0)
{
stBuf.iCount--;
printf("[Consumer %d] consumer [%c] ", num, stBuf.strArray[stBuf.iCount]);
stBuf.strArray[stBuf.iCount] = 0;
printf("%s\n", stBuf.strArray);
}
pthread_mutex_unlock(&g_mutex);
Sleep((rand() % 200) * 10);//in windows system, Sleep() is ms
//usleep((rand() % 200) * 1000);//Linux use
}
return NULL;
}
/*!
* @brief producer model function
*
* @param[in] arg the serial number
*
* @return NULL to end
*/
void* producer(void* arg)
{
printf("%d ", pthread_self());
if (NULL == arg)
{
exit(-1);
}
int num = *(int*)arg;
char ch = 0;
while (1)
{
pthread_mutex_lock(&g_mutex);
if (stBuf.iCount < MAX_BUDDER_SIZE)
{
ch = getRandLetter();
printf("[Producer %d] produce [%c] ", num, ch);
stBuf.strArray[stBuf.iCount++] = ch;
printf("%s\n", stBuf.strArray);
}
pthread_mutex_unlock(&g_mutex);
Sleep((rand() % 100) * 10);//in windows system, Sleep() is ms
//usleep((rand() % 100) * 1000);//Linux use
}
return NULL;
}
三、实验结果
存在问题:VS2019下的两个生产者不论相隔多远,都会产生同样的随机字母,问题无法找出,怀疑是pthread.h相关兼容性问题产生。或者是rand()函数的问题?或者是Sleep函数的问题?
1.VS2019下生成的结果
2.linux下生成结果