Сидоров Георгий Александрович - лекции, семинары
Почта: georgiy.sidorov@gmail.com
Telegram: @georgiysidorov80
Семнары: (22207 22208 22209)
Гамарник Андрей Янович - семинары
Почта: mvcdev@ya.ru
Telegram: @mvcdev
Семинары: (22204 22205 22206)
Анисимов Кирилл Александрович - семинары
Почта: anisimov_kirill@mail.ru
Telegram: @ankirsansan
Общий контекст
Задача
5 философов сидят за круглым столом. Между каждой парой соседей лежит одна вилка (всего 5 вилок). Философ проводит время в размышлениях, но периодически ему нужно поесть. Чтобы поесть, философу необходимы ОБЕ вилки - левая и правая. После еды философ может положить вилки обратно на стол.
Состояния философа:
Thinking - размышления, не имеет вилок
Hungry - проголодался, пытается взять обе вилки
Eating - ест
Состояния вилки:
Available - доступна для взятия
InUse - используется философом для еды или находится в процессе взятия/возврата
Процесс
Философ становится голодным (Hungry) через определенное время, это время случайно определяется согласно условиям задачи.
Для того чтобы перейти в состояние Eating, нужно взять обе вилки. Процесс взятия не мгновенный, вилки берутся по очереди, порядок взятия определяет стратегия. Философ может положить уже взятую вилку, если нужно - это происходит мгновенно.
Философ ест определенное время, после чего возвращает вилку на стол и переходит в состояние Thinking.
Вилки мгновенно возвращаются на стол.
Отображение состояния
В различных состояние необходимо выводить в консоль состояние симуляции
===== ШАГ 150 =====
Философы:
Платон: Thinking (Action = ReleaseRightFork|ReleaseLeftFork), съедено: 22
Аристотель: Thinking, съедено: 233
Сократ: Eating (4 steps left), съедено: 33
Декарт: Thinking (3 steps left), съедено: 22
Кант: Hungry (Action = TakeRightFork|TakeLeftFork|ReleaseRightFork|ReleaseLeftFork|None), съедено: 33
Вилки:
Fork-1: Available
Fork-2: Available
Fork-3: InUse (используется Аристотель)
Fork-4: InUse (используется Сократ)
Fork-5: InUse (используется Декарт)
Стратегия и координатор:
Важно: Стратегия не должна знать состояние всех философов и всех вилок. Только 2 вилки рядом.
В некоторых задачах (1-я и 7-я) требуется использование координатора. Координатор знает про всех философов и все вилки. Взаимодействие философа и координатора должно быть событийным. Философ, с помощью синхронного вызова декларирует свое желание поесть, а координатор отправляет событие "возьми ту или иную вилку", когда это становится возможным.
Метрики
Пропускная способность - количество съеденного в единицу времени, по каждому философу и среднее.
Время ожидания - время нахождения в состоянии Hungry, среднее по всем философам, максимальное с указанием философа, на котором оно было достигнуто.
Коэффициент утилизации для каждой вилки - сколько процентов времени вилка свободна/заблокирована/используется для еды.
Задача 1: ООП - Пошаговая симуляция
Описание
Реализовать консольное приложение с пошаговой симуляцией (без многопоточности). Каждый шаг симуляции обрабатывает состояния всех философов последовательно.
Требования
Имена философов считывать из файла
Симуляция длится 1000000 шагов
Разработать контракт стратегии и координатора, они должны быть помещены в отдельный проект.
Реализовать наивную стратегию без координатора. Реализовать любую стратегию с координатором (передача информации о том что пилософ может сделать дожна использовать события языка C#).
Реализация стратегии и координатора должна быть выделена в отдельный проект.
Определять deadlock (взаимная блокировка всех философов).
Длительность состояний, для многопоточной симуляции:
Длительность размышлений: 3-10 шага(случайно)
Длительность еды: 4-5 шага(случайно)
Взятие каждой вилки: 2 шага
Освобождение вилок: мгновенно (обе вилки одновременно)
Метрики для Задачи 2 (и далее)
Уточнение по метрикам
Пропускная способность измеряется в количестве съеденного за 1000 шагов
Время ожидания измеряется в количестве шагов
В конце вывести Score (общее количество съеденного всеми философами) и все метрики
Задача 2: Многопоточная симуляция
Описание
Реализовать консольное приложение с многопоточной симуляцией в реальном времени. Реализуем стратегию без координатора
Требования
Каждый философ работает в отдельном потоке
Длительность симуляции должна задаваться константой
Отображение состояни через каждые 100-200 мс
Длительность состояний, для многопоточной симуляции:
Длительность размышлений: 30-100 мс (случайно)
Длительность еды: 40-50 мс (случайно)
Взятие вилки: 20 мс
Освобождение вилок: мгновенно (обе вилки одновременно)
Метрики для Задачи 2 (и далее):
Пропускная способность (еда/миллисекунда)
Среднее время ожидания для каждого философа (в миллисекундах)
Коэффициент утилизации для каждой вилки в % по времени
Задача 3: Использование .NET Generic Host
Описание
Реализовать консольное приложение с использованием .NET Generic Host, где каждый философ представлен отдельным Hosted Service. Использовать встроенные возможности .NET для управления жизненным циклом сервисов, логирования и конфигурации.
Требования
Каждый философ реализован как отдельный BackgroundService
Использовать Dependency Injection для всех компонентов
Конфигурация через appsettings.json и IOptions
Использовать IHostApplicationLifetime для того, чтобы прервать выполнение программы
Аккуратно останавливать работу после истечения времени, использовать CancellationToken для этого
Сервисы:
PhilosopherHostedService - основной сервис для каждого философа
IPhilosopherStrategy - Стратегия философа
Возможно другие сервисы: калькулятор метрик, менеджер вилок и прочее
Program.cs пример
services.AddSingleton<ITableManager, TableManager>();
services.AddSingleton<IMetricsCollector, MetricsCollector>();
services.AddSingleton<IPhilosopherStrategy, LeftRightStrategy>();
//возможно другие
static PhilosopherHostedService CreatePhilosopher(IServiceProvider provider, string name) => //Реализация
// Регистрируем каждого философа
services.AddHostedService(provider => CreatePhilosopher(provider, "Платон"));
services.AddHostedService(provider => CreatePhilosopher(provider, "Аристотель"));
services.AddHostedService(provider => CreatePhilosopher(provider, "Сократ"));
services.AddHostedService(provider => CreatePhilosopher(provider, "Декарт"));
services.AddHostedService(provider => CreatePhilosopher(provider, "Кант"));
services.Configure<SimulationOptions>(configuration.GetSection("Simulation"));
**Конфигурация (appsettings.json): пример **
{
"Simulation": {
"DurationSeconds": 120,
"ThinkingTimeMin": 30,
"ThinkingTimeMax": 100,
"EatingTimeMin": 40,
"EatingTimeMax": 50,
"ForkAcquisitionTime": 20,
"DisplayUpdateInterval": 200
}
}
Задача 4: Unit тестирование
Описание
Написать комплексные Unit тесты для основных компонентов системы обедающих философов. Тесты должны покрывать бизнес-логику, стратегии принятия решений и метрики.
Требования
Использовать xUnit или nUnit как основной фреймворк для тестирования
Можно использовать Moq для создания mock-объектов
Тесты должны быть изолированными и независимыми
Компоненты для тестирования
1. Симуляция
Основные сценарии:
Переходы между состояниями (Thinking → Hungry → Eating → Thinking), с учетом наличия вилок.
2. Стратегии
Основные сценарии:
Правильный порядок взятия вилок, для нативной стратегии
Правильный порядок взятия вилок, для стратегии "Иерархия ресурсов"
3. Deadlock
Основные сценарии для тестирования:
Проверка попадания в deadlock в специально подобранных условиях
Задача 5: Базы данных
Описание
Реализовать систему сохранения состояния симуляции в базе данных с возможностью запроса состояния в любой момент времени. Система должна записывать все изменения состояний философов и вилок.
Требования
Функциональные требования
Во время выполнения симуляции состояние должно сохранаться в базе данных, каждому выполнению должен быть присвоен уникальный runId, runId должен быть распечатан в консоли
Необходимо консольное приложение DiningPhilosophers.View, которое параметром получает runId и смещение в секундах относительно начала (например, в виде --runId 33 --delay 44.12) и распечатывает состояние стола на этот момент
Технические требования
Использовать PostgreSQL как основную СУБД
Использовать Entity Framework Core для работы с базой данных
Применить Code First подход с миграциями
Написать тесты с использованием InMemory SQLite
Задача 6: Микросервисы
Описание
Реализовать систему обедающих философов в виде микросервисной архитектуры, где каждый философ и стол являются отдельными HTTP-сервисами, взаимодействующими через REST API. Вся система должна запускаться через Docker Compose. Сохранение в БД не нужно.
Архитектура
Сервисы
Table Service (Стол) - центральный сервис, управляющий состоянием вилок и сбором метрик
Philosopher Service - отдельный экземпляр для каждого философа (5 сервисов)
Требования
Технические требования
Использовать ASP.NET Core Web API для всех сервисов
Взаимодействие только через HTTP REST API
Каждый сервис в отдельном Docker контейнере
Запуск всей системы через Docker Compose
Использовать HttpClient для межсервисного взаимодействия
Необходимо разработать контракты взаимодействия между микросервисами
Docker Compose
Необходимо создать docker-compose.yml файл, который запускает:
1 экземпляр Table Service
5 экземпляров Philosopher Service (по одному для каждого философа)
Каждый философ должен отработать настроенное количество минут, и сообщить столу о своем выходе. После того как все философы вышли, Table Service должен распечатать итоговые метрики.
services:
table-service:
build: ./TableService
ports:
- "8080:8080"
environment:
- ASPNETCORE_URLS=http://+:8080
- PHILOSOPHERS_COUNT=5
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
philosopher-plato:
build: ./PhilosopherService
environment:
- ASPNETCORE_URLS=http://+:8081
- PHILOSOPHER_NAME=Платон
- PHILOSOPHER_ID=philosopher-1
- LEFT_FORK_ID=1
- RIGHT_FORK_ID=2
- TABLE_SERVICE_URL=http://table-service:8080
- SIMULATION_DURATION_MINUTES=5
depends_on:
table-service:
condition: service_healthy
ports:
- "8081:8081"
philosopher-aristotle:
...
Задача 7: Event-Driven микросервисы с координатором
Описание
Реализовать продвинутую микросервисную систему обедающих философов с координатором, использующую гибридную архитектуру: HTTP API для синхронных операций и асинхронные события для координации. Система должна использовать MassTransit или аналогичную библиотеку для обмена сообщениями, либо RabbitMQ Client Library
Архитектура
Сервисы
Table Service - управляет состоянием вилок, обрабатывает HTTP запросы
Philosopher Service - отдельный экземпляр для каждого философа (5 сервисов)
Coordinator Service - координирует доступ к ресурсам через события
Message Broker - RabbitMQ для обмена событиями
Логика координации
Координатор должен реализовать любую стратегию предотвращения deadlock. Требования к взаимодействию:
Операции взаимодействия философов и стола должны быть синхронными (HTTP API)
Операции взаимодействия философов и координатора должны быть событийными, с использованием MassTransit, аналогов или нативного API.