BUAA-OS-2021 进程同步-部分代码一览
Problem 1
题面
三个进程 P1
、P2
、P3
互斥使用一个包含N(N>0)
个单元的缓冲区。P1
每次用produce()
生成一个正整数并用put()
送入缓冲区某一个空单元中;P2
每次用 getodd()
从该缓冲区中 取出一个奇数并用 countodd()
统计奇数个数;P3
每次用geteven()
从该缓冲区中取出一个 偶数并用 counteven()
统计偶数个数。请用信号量机制实现这三个进程的同步与互斥活动, 并说明所定义的信号量的含义。
参考代码
Windows
平台,语言C++
,编译建议g++
。
#include <windows.h>
#include <array>
#include <iostream>
#define ENABLE_LOG
#define P(X) WaitForSingleObject(X, INFINITE)
#define V(X) ReleaseSemaphore(X, 1, NULL)
using Int = int;
const size_t N = 10;
const Int Nil = 0;
static std::array<Int, N> buf;
static unsigned int odd_counter = 0, even_counter = 0;
Int produce()
{
static Int counter = 0;
return ++counter;
}
void put(Int element)
{
for (auto &&val : buf)
if (val == Nil)
{
val = element;
break;
}
}
void getodd()
{
for (auto &&val : buf)
if (val != Nil && (val & 1) == 1)
{
val = 0;
break;
}
}
void geteven()
{
for (auto &&val : buf)
if (val != Nil && (val & 1) == 0)
{
val = 0;
break;
}
}
void countodd()
{
++odd_counter;
}
void counteven()
{
++even_counter;
}
HANDLE bufMutex, bufNotFull, bufHasOdd, bufHasEven; // 信号量(Semaphore)
unsigned long WINAPI P1(LPVOID lpParameter)
{
static const DWORD P1_SPEED = 1000; // 生产速度
while (true)
{
P(bufNotFull);
Int product = produce();
P(bufMutex);
put(product);
#ifdef ENABLE_LOG
std::cout << "[P1] produce and put " << product << " to the buffer" << std::endl;
#endif
V(bufMutex);
if ((product & 1) == 1)
V(bufHasOdd);
else
V(bufHasEven);
Sleep(P1_SPEED);
}
}
unsigned long WINAPI P2(LPVOID lpParameter)
{
static const DWORD P2_SPEED = 3000; // 消费奇数速度
while (true)
{
P(bufHasOdd);
P(bufMutex);
getodd();
V(bufMutex);
V(bufNotFull);
countodd();
#ifdef ENABLE_LOG
std::cout << " [P2] consume number and odd counter now is " << odd_counter << std::endl;
#endif
Sleep(P2_SPEED);
}
}
unsigned long WINAPI P3(LPVOID lpParameter)
{
static const DWORD P3_SPEED = 3000; // 消费偶数速度
while (true)
{
P(bufHasEven);
P(bufMutex);
geteven();
V(bufMutex);
V(bufNotFull);
counteven();
#ifdef ENABLE_LOG
std::cout << " [P3] consume number and even counter now is " << even_counter << std::endl;
#endif
Sleep(P3_SPEED);
}
}
int main()
{
bufMutex = CreateSemaphore(NULL, 1, 1, NULL); // 信号量安全特性;初始计数;最大计数;名称
bufNotFull = CreateSemaphore(NULL, N, N, NULL);
bufHasOdd = CreateSemaphore(NULL, 0, N, NULL);
bufHasEven = CreateSemaphore(NULL, 0, N, NULL);
unsigned long thread1Id = 0, thread2Id = 0, thread3Id = 0;
HANDLE thread1 = CreateThread(NULL, 0, P1, NULL, 0, &thread1Id);
HANDLE thread2 = CreateThread(NULL, 0, P2, NULL, 0, &thread2Id);
HANDLE thread3 = CreateThread(NULL, 0, P3, NULL, 0, &thread3Id);
getchar(); // 回车以终止程序
return 0;
}
Problem 2
题面
一个野人部落从一个大锅中一起吃炖肉,这个大锅一次可以存放 M
人份的炖肉。当野人们想吃的时候,如果锅中不空,他们就自助着从大锅中吃肉。如果大锅空了,他们就叫 醒厨师,等待厨师再做一锅肉。
野人线程未同步的代码如下:
while (true) {
getServingFromPot();
eat();
}
厨师线程未同步的代码如下:
while (true) {
putServingsInPot(M);
}
同步的要求是: 当大锅空的时候,野人不能够调用getServingFromPot()
仅当大锅为空的时候,大厨才能够调用 putServingsInPot()
问题:请写出使用 PV
满足同步要求的完整程序。
参考代码
Windows
平台,语言Pascal
,编译建议fpc
。
{$DEFINE ENABLE_LOG}
uses windows, gqueue;
type
Meat = record
end;
MeatQueue = specialize TQueue<Meat>;
const M: Integer = 10;
var
pot : MeatQueue;
haveFood, isEmpty : HANDLE;
procedure getServingFromPot();
begin
if pot.IsEmpty then
begin
writeln('------!panic(the pot is empty.)------');
halt;
end;
pot.Pop
end;
procedure putServingsInPot(n:Integer);
var
i:Integer;
niku: Meat;
begin
for i:= 1 to n do
pot.Push(niku)
end;
procedure P(X:HANDLE);
begin
WaitForSingleObject(X, INFINITE)
end;
procedure V(X:HANDLE);
begin
ReleaseSemaphore(X, 1, nil)
end;
function Wildling(lpParameter:LPVOID): DWORD; stdcall;
const
wildling_time : DWORD = 500;
begin
while True do begin
P(haveFood);
getServingFromPot();
Sleep(wildling_time);
if pot.IsEmpty then
V(isEmpty)
else
V(haveFood);
{$IFDEF ENABLE_LOG}
writeln(' [Wildling] eat one, rest meat number = ', pot.Size);
{$ENDIF}
end
end;
function Chef(lpParameter:LPVOID): DWORD; stdcall;
const
chef_time : DWORD = 3000;
begin
while True do begin
P(isEmpty);
putServingsInPot(M);
Sleep(chef_time);
{$IFDEF ENABLE_LOG}
writeln('[chef] produce ', M, ' meat');
{$ENDIF}
V(haveFood)
end
end;
var
thread_wildling, thread_chef: HANDLE;
thread_wildling_id, thread_chef_id: DWORD;
begin
pot := MeatQueue.Create;
isEmpty := CreateSemaphore(nil, 1, 1, nil);
haveFood := CreateSemaphore(nil, 0, 1, nil);
thread_wildling := CreateThread(nil, 0, @Wildling, nil, 0, thread_wildling_id);
thread_chef := CreateThread(nil, 0, @Chef, nil, 0, thread_chef_id);
readln;
pot.Destroy;
end.