BUAA-OS-2021 进程同步-部分代码一览

Problem 1

题面

三个进程 P1P2P3 互斥使用一个包含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.
posted @ 2021-04-19 22:49  buaa-shy  阅读(162)  评论(0编辑  收藏  举报