ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»
(СПбГУТ)
Кафедра программной инженерии и вычислительной техники
Отчёт
по лабораторной работе №10 на тему:
«Моделирование систем»
по дисциплине «Операционные системы»
Выполнил: студент группы ИКВТ-61, Козырев А.Б.
« |
|
» |
|
|
2019 |
г. ___________/А.Б. Козырев/ |
|
Принял: __к.т.н. |
Дагаев А.В. |
||||||
« |
|
» |
|
|
2019 |
г. ___________/_А.В. Дагаев/ |
САНКТ-ПЕТЕРБУРГ
2019
Задание: разработать приложение, моделирующее систему или стратегию работы системы (напр. стратегия: обслуживания процессов в ОС, обслуживания системы, замещения страниц в ОП, обслуживание заявок на выборку данных HD; моделирование системы массового обслуживания и др.)
QThreads и QSemaphore для “производителя и потребителя”.
Эта программа показывает, как использовать QSemaphore для управления доступом к циклическому буферу, совместно используемому потоком производителя и потоком потребителя.
Производитель записывает данные в буфер, пока не достигнет конца буфера, после чего он перезапускается с самого начала, перезаписывая существующие данные. Поток потребителя читает данные по мере их поступления и записывает их в стандартную ошибку.
Семафоры позволяют иметь более высокий уровень параллелизма, чем мьютексы. Если бы доступ к буферу был защищен QMutex, то поток потребителя не смог бы получить доступ к буферу в то же время, что и поток производителя. Тем не менее, нет ничего плохого в том, что оба потока работают над разными частями буфера одновременно.
Программа состоит в основном из трех классов: Producer, Consumer и класс QDialog. Оба наследуются от QThread. Круговой буфер, используемый для связи между этими двумя классами и семафорамы, которые его защищают, являются глобальными переменными.
Глобальные переменные Использованиесемафоровгарантирует,чтопроизводительникогданеопережает
потребителя больше, чем байты BufferSize, и что потребитель никогда не читает данные, которые производитель еще не сгенерировал. Другими словами, мы используем QSemaphore для управления доступом к циклическому буферу, совместно используемому потоком производителя и потоком потребителя.
Для достижения цели мы используем глобальные переменные:
const int DataSize = 100000;
const int BufferSize = 8192; char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
DataSize - это объем данных, которые сгенерирует производитель. Чтобы сделать пример как можно более простым, мы сделаем его постоянным. BufferSize
- размер кольцевого буфера. Это меньше, чем DataSize, что означает, что в ка- кой-то момент производитель достигнет конца буфера и перезапустится с самого начала.
Чтобы синхронизировать производителя и потребителя, нам нужны два семафора. Семафор freeBytes контролирует «свободную» область буфера (область, которую производитель еще не заполнил данными или которую потребитель уже прочитал). Семафор usedBytes управляет «используемой» областью буфера (область, заполненная производителем, но которую потребитель еще не прочитал).
Семафор freeBytes инициализируется с помощью BufferSize, потому что изначально весь буфер пуст. Семафор usedBytes инициализируется значением 0 (значение по умолчанию, если оно не указано).
Класс Producer
Класс производителя выглядит так:
#include "producer.h"
#include "common.h"
#include <QDebug> Producer::Producer(QObject *parent) :
QThread(parent)
{
}
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); for (int i = 0; i < DataSize; ++i) {
freeBytes.acquire();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4]; usedBytes.release();
if(i % 20 == 0)
emit bufferFillCountChanged(usedBytes.available()); emit producerCountChanged(i);
}
}
Производитель генерирует байты данных DataSize. Прежде чем записать байт в циклический буфер, он должен получить «свободный» байт, используя семафор freeBytes. Вызов QSemaphore :: acquire () может блокироваться, если потребитель не поспевает за производителем.
В конце производитель выпускает байт, используя семафор usedBytes. «Свободный» байт был успешно преобразован в «использованный» байт, готовый для чтения потребителем.
UsedBytes.available () предназначен для пользовательского интерфейса. Возвращает доступные байты для потребителя.
Этот класс испускает два сигнала: один для количества произведенных байтов и уровень байтов, доступных для потребления.
Класс Consumer
Код почти симметричен классу Producer, за исключением того, что на этот раз мы получаем «используемый» байт и освобождаем «свободный» байт вместо противоположного.
#include "consumer.h" #include "common.h"
Consumer::Consumer(QObject *parent) : QThread(parent)
{
}
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) { usedBytes.acquire();
fprintf(stderr, "%c", buffer[i % BufferSize]); freeBytes.release();
emit bufferFillCountChanged(usedBytes.available()); emit consumerCountChanged(i);
}
fprintf(stderr, "\n");
}
Класс Dialog
Когда программа запускает QDialog, в конструкторе создаются два новых объекта QThread. Затем, когда нажимается кнопка «Пуск», два потока вызывают start ().
#include "conproddialog.h"
#include "ui_conproddialog.h"
#include "myConstants.h"
// BufferSize: maximum bytes that can be stored char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
ConProdDialog::ConProdDialog(QWidget *parent) : QDialog(parent),
ui(new Ui::ConProdDialog)
{
ui->setupUi(this);
// progress bar range setup ui->producerProgressBar->setRange(0, DataSize); ui->consumerProgressBar->setRange(0, DataSize); ui->bufferProgressBar->setRange(0, BufferSize);
// make two threads
mProducer = new Producer(this); mConsumer = new Consumer(this);
// connect signal/slot for the buffer progress bar connect(mConsumer, SIGNAL(bufferFillCountChanged(int)),
this, SLOT(onBufferValueChanged(int)));
connect(mProducer, SIGNAL(bufferFillCountChanged(int)), this, SLOT(onBufferValueChanged(int)));
// connect signal/slot for consumer/producer progress bar connect(mConsumer, SIGNAL(consumerCountChanged(int)),
this, SLOT(onConsumerValueChanged(int)));
connect(mProducer, SIGNAL(producerCountChanged(int)), this, SLOT(onProducerValueChanged(int)));
}
ConProdDialog::~ConProdDialog()
{
delete ui;
}
void ConProdDialog::onBufferValueChanged(int bCount)
{
ui->bufferProgressBar->setValue(bCount);
}
void ConProdDialog::onProducerValueChanged(int pCount)
{
ui->producerProgressBar->setValue(pCount);
}
void ConProdDialog::onConsumerValueChanged(int cCount)
{
ui->consumerProgressBar->setValue(cCount);
}
// start button clicked
void ConProdDialog::on_startButton_clicked()
{
// disable the start button ui->startButton->setEnabled(false);
// threads starat mProducer->start(); mConsumer->start();
}
Пользовательский интерфейс выглядит так:
Покажем листинг остальных файлов:
main.cpp
#include "conproddialog.h"
#include <QApplication>
#include "conproddialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv); ConProdDialog w;
w.setWindowTitle("Semaphore: Consumer & Producer"); w.show();
return a.exec();
}
common.h
#ifndef COMMON_H
#define COMMON_H
#include <QSemaphore>
#include "myConstants.h"
extern char buffer[BufferSize];
extern QSemaphore freeBytes; extern QSemaphore usedBytes;
#endif // COMMON_H
myConstants.h:
#ifndef MYCONSTANTS_H
#define MYCONSTANTS_H
const int DataSize = 100000; const int BufferSize = 8192;
#endif // MYCONSTANTS_H
conproddialog.h:
#ifndef CONPRODDIALOG_H #define CONPRODDIALOG_H
#include <QDialog> #include "consumer.h" #include "producer.h"
#include <QSemaphore>
namespace Ui {
class ConProdDialog;
}
class ConProdDialog : public QDialog
{
Q_OBJECT
public:
explicit ConProdDialog(QWidget *parent = 0); ~ConProdDialog();
public slots:
void onBufferValueChanged(int); void onProducerValueChanged(int); void onConsumerValueChanged(int);
private slots:
void on_startButton_clicked();
private:
Ui::ConProdDialog *ui; Producer *mProducer; Consumer *mConsumer;
};
#endif // CONPRODDIALOG_H
producer.h:
#ifndef PRODUCER_H
#define PRODUCER_H
#include <QThread>
#include <QTime>
class Producer : public QThread
{
Q_OBJECT
public:
explicit Producer(QObject *parent = 0); void run();
signals:
void bufferFillCountChanged(int bCount); void producerCountChanged(int count);
public slots: };
#endif // PRODUCER_H
consumer.h:
#ifndef CONSUMER_H #define CONSUMER_H
#include <QThread> #include <QTime>
class Consumer : public QThread
{
Q_OBJECT public:
explicit Consumer(QObject *parent = 0); void run();
signals:
//void stringConsumed(const QString &text); void bufferFillCountChanged(int cCount); void consumerCountChanged(int count);
public slots: };
#endif // CONSUMER_H
и наконец, .pro
ConProd2.pro:
#-------------------------------------------------
#
# Project created by QtCreator 2013-09-27T15:43:43
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ConProd2
TEMPLATE = app
SOURCES += main.cpp\ conproddialog.cpp \
consumer.cpp \ producer.cpp
HEADERS += conproddialog.h \ consumer.h \
producer.h \ common.h \ myConstants.h
FORMS |
+= conproddialog.ui |
Результат работы программы