实验一
一、实验内容或题目:
在老师给的代码基础上,按照课本3.6.3的代码意图,实现一个生产者和一个消费者的同步问题。
二、实验目的与要求:
上传的producer-consumer.c已经编写了生产者和消费者线程的基础工作内容。请将之更改成多线程完成工作的方式,使两个线程可以正确的同步“生产-消费”行为。
三、实验步骤:
根据示例代码,实现两个进程向队列添加和取出的同步操作。
需要使用的api:_beginthread, CreateEvent, WaitForSingleObject, SetEvent。
四、实验结果:
达到了预期效果,消费者进程和生产者进程实现同步,程序持续运行。
五、总结:
Windows有四种同步同步方式,分别是临界区、事件、信号量、互斥量。该实验中使用的是设置事件的方式,首先说明一下所需要用到的api。_beginthread:创建线程;CreateEvent:创建一个事件;SetEvent:将事件变为有信号可使用状态;ResetEvent:将事件信号进行复位,变为无信号;WaitForSingleObject:等待,直到事件变为有信号状态,如果使用了一个事件,而不对它有任何操作,那么该进程将一直无法运行,因为一个事件默认是无信号状态,该api会使该进程一直等下去,当有信号时函数便会放行,无信号时,此函数将会一直阻塞。
由与示例代码稍微有一些复杂,所以我先改写了一下代码,简单说明这些api的使用。首先设一个全局变量为a,生产者进程就是每次在循环中执行a++,消费者进程就是每次在循环中执行a–,两个的退出循环条件都是当a等于0时退出,实现目标就是保证生产者进程在消费者进程之前执行,也就是实现同步。首先在main中创建一个事件hFull,在消费者进程增加一个判断,当a==1时执行WaitForSingleObject,等待hFull,由于hFull默认无信号(CreateEvent第三个参数为false),所以函数会一直等下去,直到生产者进程执行了SetEvent,将hull信号变为有信号,WaitForSingleObject才会执行接下来的代码,这样也就保证了生产者和消费者一直持续运行。如果不在生产者进程中写SetEvent,那么消费者会不在执行,一直等待。
关于CreateEvent的参数,其中当第二个参数为true时,表示其在WaitForSingleObject后必须手动调用ResetEvent清除信号,如果为false,表示其在WaitForSingleObject后系统自动清除事件信号;第三个参数为false,表示初始都为无信号状态;
经过上面简单代码的分析,那么示例代码很容易看懂了,说明一下示例代码的逻辑,当队列为空时,在进入消费者进程中,会发动对hFull的WaitForSingleObject那么消费者进程将会被挂起,不在执行。在进入生产者进程后由于needWake为空,所以会对hFull发动SetEvent,此时队列已经添加进了元素,同时hFull也被赋予了信号,消费者进程又可以正常执行。队列满时同理。
实验二
一、实验内容或题目:
在实验一的基础上,按照课本3.6.4的代码意图,实现多个生产者和多个消费者的同步问题。
二、实验目的与要求:
实现多生产和多消费者之间的正确同步
三、实验步骤:
利用示例代码,改写变为多生产和多消费者之间的同步。
需要使用的api:CreateMutex, ReleaseMutex。
四、实验结果:
通过利用互斥量成功实现多个生产者和多个消费者的同步问题。
五、总结
如果单纯在实验一代码的基础上多添加连两个线程,可以发现两个生产者同时向0各自添加了产品,这显然是错误的。为了避免两个生产者或两个消费者同时操作列表,需要引入互斥量。采用互斥对象的机制,这样使得只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。
首先通过CreateMutex分别为生产者和消费者创建两个互斥量。在每次循环的第一句对hMutex执行WaitForSingleObject,这样就使得两个生产者或两个消费者不同时执行,在循环的最后用ReleaseMutex释放互斥量,使得等待的另一个生产者或消费者可以开始执行,这样就实现多个生产者和多个消费者的同步问题。
附录
multi.cpp
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include <math.h>
#include <time.h>
#define POOL_SIZE 10
unsigned int Pool[POOL_SIZE];
unsigned int PoolHeader = 0;
unsigned int PoolTail = 0;
#define POOL_FULL (((PoolHeader + 1) % POOL_SIZE) == PoolTail)
#define POOL_EMPTY (PoolHeader == PoolTail)
HANDLE hEmpty;
HANDLE hFull;
HANDLE hMutex_producer; //定义互斥对象句柄
HANDLE hMutex_consumer;
/**
生产者线程
**/
unsigned __stdcall producer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
WaitForSingleObject(hMutex_producer, INFINITE);
if (!POOL_FULL) {
printf("生产者【%d】向【%d】格子里添加了一个产品\n", threadId, PoolHeader);
Pool[PoolHeader] = 1;
BOOL needWake = POOL_EMPTY;
PoolHeader = (PoolHeader + 1) % POOL_SIZE;
if (needWake) {
SetEvent(hFull);
}
}
else {
printf("生产者【%d】开始等待\n", threadId);
WaitForSingleObject(hEmpty, INFINITE);
}
const unsigned int rest = rand() % 2000;
Sleep(rest);
ReleaseMutex(hMutex_producer);
} while (TRUE);
}
/**
消费者线程
**/
unsigned __stdcall consumer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
WaitForSingleObject(hMutex_consumer, INFINITE);
if (!POOL_EMPTY) {
printf("消费者【%d】从【%d】格子里取走了一个产品\n", threadId, PoolTail);
Pool[PoolTail] = 1;
BOOL needWake = POOL_FULL;
PoolTail = (PoolTail + 1) % POOL_SIZE;
if (needWake) {
SetEvent(hEmpty);
}
}
else {
printf("消费者【%d】开始等待\n", threadId);
WaitForSingleObject(hFull, INFINITE);
}
const unsigned int rest = rand() % 2000;
Sleep(rest);
ReleaseMutex(hMutex_consumer);
} while (TRUE);
}
int main() {
// 创建事件
hEmpty = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
// 判断是否创建成功
if (hEmpty == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 0;
}
hFull = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (hFull == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 0;
}
// 创建互斥量
hMutex_producer = CreateMutex(NULL, false, NULL);
hMutex_consumer = CreateMutex(NULL, false, NULL);
// 设定随机数种子
srand((unsigned int)time(NULL));
//创建进程
HANDLE hThreads[4];
hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, &producer, NULL, 0, NULL);
hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, &consumer, NULL, 0, NULL);
hThreads[2] = (HANDLE)_beginthreadex(NULL, 0, &producer, NULL, 0, NULL);
hThreads[3] = (HANDLE)_beginthreadex(NULL, 0, &consumer, NULL, 0, NULL);
// 无线等待进程结束
WaitForMultipleObjects(4, hThreads, TRUE, INFINITE);
CloseHandle(hThreads);
CloseHandle(hFull);
CloseHandle(hEmpty);
CloseHandle(hMutex_producer);
CloseHandle(hMutex_consumer);
return 0;
}
simple_example.cpp
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include <math.h>
#include <time.h>
#define POOL_SIZE 10
unsigned int Pool[POOL_SIZE];
unsigned int PoolHeader = 0;
unsigned int PoolTail = 0;
#define POOL_FULL (((PoolHeader + 1) % POOL_SIZE) == PoolTail)
#define POOL_EMPTY (PoolHeader == PoolTail)
HANDLE hEmpty;
HANDLE hFull;
int a = 1;
/**
生产者线程
**/
unsigned __stdcall producer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
a++;
printf("生产者%d\n", a);
SetEvent(hFull);
const unsigned int rest = rand() % 2000;
Sleep(rest);
} while (a != 0);
return 0;
}
/**
消费者线程
**/
unsigned __stdcall consumer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
if (a == 1)
{
printf("在等了");
WaitForSingleObject(hFull, INFINITE);
}
a--;
printf("消费者%d\n", a);
const unsigned int rest = rand() % 2000;
Sleep(rest);
} while (a != 0);
return 0;
}
void main() {
hFull = CreateEvent(
NULL, // default security attributes
FALSE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent_two") // object name
);
if (hFull == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// 设定随机数种子
srand((unsigned int)time(NULL));
//创建进程
HANDLE hThreads[2];
hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, &producer, NULL, 0, NULL);
hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, &consumer, NULL, 0, NULL);
// 无线等待进程结束
WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
CloseHandle(hThreads[0]);
CloseHandle(hThreads[1]);
CloseHandle(hFull);
}
single.cpp
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include <math.h>
#include <time.h>
#define POOL_SIZE 10
unsigned int Pool[POOL_SIZE];
unsigned int PoolHeader = 0;
unsigned int PoolTail = 0;
#define POOL_FULL (((PoolHeader + 1) % POOL_SIZE) == PoolTail)
#define POOL_EMPTY (PoolHeader == PoolTail)
HANDLE hEmpty;
HANDLE hFull;
/**
生产者线程
**/
unsigned __stdcall producer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
if (!POOL_FULL) {
printf("生产者【%d】向【%d】格子里添加了一个产品\n", threadId, PoolHeader);
Pool[PoolHeader] = 1;
BOOL needWake = POOL_EMPTY;
PoolHeader = (PoolHeader + 1) % POOL_SIZE;
if (needWake) {
SetEvent(hFull);
}
}
else {
printf("生产者【%d】开始等待\n", threadId);
WaitForSingleObject(hEmpty, INFINITE);
}
const unsigned int rest = rand() % 2000;
Sleep(rest);
} while (TRUE);
}
/**
消费者线程
**/
unsigned __stdcall consumer(void* pArguments) {
const unsigned int threadId = GetCurrentThreadId();
do {
if (!POOL_EMPTY) {
printf("消费者【%d】从【%d】格子里取走了一个产品\n", threadId, PoolTail);
Pool[PoolTail] = 1;
BOOL needWake = POOL_FULL;
PoolTail = (PoolTail + 1) % POOL_SIZE;
if (needWake) {
SetEvent(hEmpty);
}
}
else {
printf("消费者【%d】开始等待\n", threadId);
WaitForSingleObject(hFull, INFINITE);
}
const unsigned int rest = rand() % 2000;
Sleep(rest);
} while (TRUE);
}
int main() {
// 创建事件
hEmpty = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
// 判断是否创建成功
if (hEmpty == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 0;
}
hFull = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (hFull == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 0;
}
// 设定随机数种子
srand((unsigned int)time(NULL));
//创建进程
HANDLE hThreads[2];
hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, &producer, NULL, 0, NULL);
hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, &consumer, NULL, 0, NULL);
// 无线等待进程结束
WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
CloseHandle(hThreads[0]);
CloseHandle(hThreads[1]);
CloseHandle(hFull);
CloseHandle(hEmpty);
return 0;
}