Browse Source

Добавить 'Лекции/2.5.1200_Управление_режимом_потоков/Gurkin.py'

u23-27gurkin 1 tháng trước cách đây
mục cha
commit
641597c35b

+ 348 - 0
Лекции/2.5.1200_Управление_режимом_потоков/Gurkin.py

@@ -0,0 +1,348 @@
+import pygame
+import sys
+import random
+
+# Инициализация Pygame
+pygame.init()
+WIDTH, HEIGHT = 1024, 768
+screen = pygame.display.set_mode((WIDTH, HEIGHT))
+pygame.display.set_caption("Диспетчер потоков – управление режимами")
+clock = pygame.time.Clock()
+
+# Шрифты
+FONT_TITLE = pygame.font.Font(None, 48)
+FONT = pygame.font.Font(None, 36)
+FONT_SMALL = pygame.font.Font(None, 28)
+
+# Цвета
+BG = (20, 25, 45)
+QUEUE_BG = (35, 45, 65)
+TEXT_COL = (255, 255, 255)
+BUTTON_COL = (70, 130, 200)
+BUTTON_HOVER = (100, 160, 240)
+ACTIVE_COL = (255, 80, 80)
+MODAL_BG = (0, 0, 0, 200)
+
+# ---------- Класс потока ----------
+class Thread:
+    def __init__(self, tid, priority, burst):
+        self.tid = tid
+        self.priority = priority
+        self.remaining = burst
+        self.base_color = (random.randint(100, 220), random.randint(100, 220), 255)
+        self.animation_progress = 0.0   # 0..1 для анимации выезда
+
+    def get_color(self, is_active=False, pulse=0):
+        if is_active:
+            # Пульсирующий красный
+            r = ACTIVE_COL[0] + int(pulse * 50)
+            g = ACTIVE_COL[1] - int(pulse * 30)
+            b = ACTIVE_COL[2] - int(pulse * 30)
+            return (min(255, r), max(50, g), max(50, b))
+        return self.base_color
+
+# ---------- Планировщик ----------
+class Scheduler:
+    def __init__(self, algorithm):
+        self.algorithm = algorithm   # "rr" или "priority"
+        self.queue = []
+        self.current = None
+        self.time_quantum = 3
+        self.quantum_counter = 0
+        self.interrupts_handled = 0
+        self.total_steps = 0
+
+    def add_thread(self, thread):
+        thread.animation_progress = 0.0
+        self.queue.append(thread)
+
+    def step(self):
+        self.total_steps += 1
+        if self.current is None:
+            if not self.queue:
+                return False
+            self.current = self.queue.pop(0)
+            self.quantum_counter = 0
+            return True
+
+        self.current.remaining -= 1
+        self.quantum_counter += 1
+
+        if self.current.remaining <= 0:
+            self.current = None
+            self.quantum_counter = 0
+        elif self.algorithm == "rr" and self.quantum_counter >= self.time_quantum:
+            self.queue.append(self.current)
+            self.current = None
+            self.quantum_counter = 0
+        elif self.algorithm == "priority":
+            higher = [t for t in self.queue if t.priority < self.current.priority]
+            if higher:
+                self.queue.append(self.current)
+                self.current = None
+                self.quantum_counter = 0
+        return True
+
+    def all_finished(self):
+        return self.current is None and len(self.queue) == 0
+
+# ---------- Вспомогательные функции отрисовки ----------
+def draw_button(text, x, y, w, h, color=None, hover_color=None):
+    if color is None:
+        color = BUTTON_COL
+    if hover_color is None:
+        hover_color = BUTTON_HOVER
+    mouse = pygame.mouse.get_pos()
+    click = pygame.mouse.get_pressed()
+    is_hover = (x < mouse[0] < x + w and y < mouse[1] < y + h)
+    col = hover_color if is_hover else color
+    pygame.draw.rect(screen, col, (x, y, w, h), border_radius=12)
+    pygame.draw.rect(screen, (0, 0, 0), (x + 2, y + 2, w, h), border_radius=12, width=1)
+    text_surf = FONT_SMALL.render(text, True, TEXT_COL)
+    screen.blit(text_surf, (x + w // 2 - text_surf.get_width() // 2, y + h // 2 - text_surf.get_height() // 2))
+    return is_hover and click[0]
+
+def draw_queue_animated(threads, current, pulse):
+    start_x = 80
+    start_y = 180
+    slot_w = 140
+    slot_h = 80
+    max_visible = 6
+
+    pygame.draw.rect(screen, QUEUE_BG, (50, start_y - 20, WIDTH - 100, 130), border_radius=15)
+
+    for i, thr in enumerate(threads[:max_visible]):
+        # Анимация выезда
+        if thr.animation_progress < 1.0:
+            thr.animation_progress = min(1.0, thr.animation_progress + 0.1)
+        offset = int((1 - thr.animation_progress) * slot_w)
+        x = start_x + i * slot_w - offset
+        y = start_y
+        rect = pygame.Rect(x, y, slot_w - 10, slot_h)
+        color = thr.get_color(False)
+        pygame.draw.rect(screen, color, rect, border_radius=10)
+        pygame.draw.rect(screen, (255, 255, 255), rect, width=2, border_radius=10)
+        text = FONT_SMALL.render(f"T{thr.tid} P:{thr.priority} {thr.remaining}", True, TEXT_COL)
+        screen.blit(text, (rect.x + 5, rect.y + 30))
+
+    if current:
+        # Пульсация активного потока
+        scale = 1 + pulse * 0.05
+        w = int(160 * scale)
+        h = int(80 * scale)
+        x = WIDTH // 2 - w // 2
+        y = start_y - 70 - int(pulse * 5)
+        color = current.get_color(True, pulse)
+        rect = pygame.Rect(x, y, w, h)
+        pygame.draw.rect(screen, color, rect, border_radius=15)
+        pygame.draw.rect(screen, (255, 255, 200), rect, width=3, border_radius=15)
+        text = FONT.render(f"Активен: T{current.tid} (ост.{current.remaining})", True, TEXT_COL)
+        screen.blit(text, (rect.x + rect.w // 2 - text.get_width() // 2, rect.y + rect.h // 2 - text.get_height() // 2))
+
+def modal_interrupt(scheduler):
+    """Модальное окно выбора обработки прерывания"""
+    modal_w, modal_h = 450, 220
+    modal_x = WIDTH // 2 - modal_w // 2
+    modal_y = HEIGHT // 2 - modal_h // 2
+
+    overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
+    overlay.fill((0, 0, 0, 180))
+    screen.blit(overlay, (0, 0))
+
+    pygame.draw.rect(screen, (50, 55, 80), (modal_x, modal_y, modal_w, modal_h), border_radius=20)
+    pygame.draw.rect(screen, (100, 130, 200), (modal_x, modal_y, modal_w, modal_h), width=3, border_radius=20)
+    title = FONT.render("⚠ Внешнее прерывание!", True, (255, 220, 100))
+    screen.blit(title, (modal_x + modal_w // 2 - title.get_width() // 2, modal_y + 30))
+    sub = FONT_SMALL.render("Переход в режим ядра. Что делать?", True, TEXT_COL)
+    screen.blit(sub, (modal_x + modal_w // 2 - sub.get_width() // 2, modal_y + 70))
+
+    btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
+    btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
+    pygame.display.flip()
+
+    while True:
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                pygame.quit()
+                sys.exit()
+            if event.type == pygame.MOUSEBUTTONDOWN:
+                if btn_imm:
+                    return True
+                if btn_delay:
+                    return False
+        # Обновляем состояние кнопок
+        btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
+        btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
+        pygame.display.flip()
+        clock.tick(30)
+
+# ---------- Основная функция ----------
+def main():
+    scene = 0            # 0-выбор алгоритма, 1-добавление потоков, 2-диспетчеризация, 3-прерывание (модальное), 4-статистика
+    algorithm = None
+    scheduler = None
+    next_tid = 1
+    message = ""
+    step_count = 0
+    interrupt_available = False
+    threads_added = []
+    running = True
+    pulse = 0.0
+    pulse_dir = 1
+
+    while running:
+        # Пульсация для активного потока
+        pulse += 0.05 * pulse_dir
+        if pulse >= 1.0:
+            pulse = 1.0
+            pulse_dir = -1
+        elif pulse <= 0.0:
+            pulse = 0.0
+            pulse_dir = 1
+
+        screen.fill(BG)
+
+        # Заголовок
+        title = FONT_TITLE.render("⚙ Управление режимом потоков", True, TEXT_COL)
+        screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 20))
+
+        # ------------------ СЦЕНА 0: ВЫБОР АЛГОРИТМА ------------------
+        if scene == 0:
+            prompt = FONT.render("Выберите алгоритм планирования:", True, TEXT_COL)
+            screen.blit(prompt, (WIDTH // 2 - prompt.get_width() // 2, 180))
+            if draw_button("Round Robin", WIDTH // 2 - 180, 280, 160, 60):
+                algorithm = "rr"
+                scheduler = Scheduler("rr")
+                scene = 1
+                threads_added = []
+                next_tid = 1
+            if draw_button("Приоритетное", WIDTH // 2 + 20, 280, 160, 60):
+                algorithm = "priority"
+                scheduler = Scheduler("priority")
+                scene = 1
+                threads_added = []
+                next_tid = 1
+            if draw_button("Выход", WIDTH - 120, HEIGHT - 70, 100, 50):
+                running = False
+
+        # ------------------ СЦЕНА 1: ДОБАВЛЕНИЕ ПОТОКОВ ------------------
+        elif scene == 1:
+            info = FONT_SMALL.render("Добавьте минимум 3 потока (случайные параметры)", True, TEXT_COL)
+            screen.blit(info, (WIDTH // 2 - info.get_width() // 2, 120))
+            if draw_button("+ Добавить поток", WIDTH - 200, HEIGHT - 100, 180, 50):
+                new = Thread(next_tid, random.randint(1, 5), random.randint(4, 12))
+                scheduler.add_thread(new)
+                threads_added.append(new)
+                next_tid += 1
+                message = f"✅ Поток T{new.tid} (приор.{new.priority}, burst {new.remaining}) добавлен"
+
+            # Список добавленных потоков
+            y = 200
+            for thr in threads_added:
+                txt = FONT_SMALL.render(f"T{thr.tid} | Приоритет: {thr.priority} | Остаток: {thr.remaining}", True, TEXT_COL)
+                screen.blit(txt, (60, y))
+                pygame.draw.circle(screen, thr.base_color, (40, y + 12), 10)
+                y += 45
+
+            if len(threads_added) >= 3:
+                if draw_button("▶ Запустить планирование", WIDTH // 2 - 120, HEIGHT - 100, 240, 50):
+                    scene = 2
+                    step_count = 0
+                    interrupt_available = False
+
+            # Сообщение и кнопка назад
+            msg_surf = FONT_SMALL.render(message, True, (200, 220, 100))
+            screen.blit(msg_surf, (50, HEIGHT - 60))
+            if draw_button("Назад", 50, HEIGHT - 60, 100, 45):
+                scene = 0
+
+        # ------------------ СЦЕНА 2: ДИСПЕТЧЕРИЗАЦИЯ ------------------
+        elif scene == 2:
+            draw_queue_animated(scheduler.queue, scheduler.current, pulse)
+
+            if draw_button("⏩ Шаг (квант)", WIDTH - 200, HEIGHT - 100, 170, 55):
+                scheduler.step()
+                step_count += 1
+                if scheduler.all_finished():
+                    scene = 4
+
+            if step_count >= 4 and not interrupt_available:
+                if draw_button("⚠ ПРЕРЫВАНИЕ", WIDTH - 200, HEIGHT - 180, 170, 50, (180, 60, 60), (220, 80, 80)):
+                    scene = 3
+
+            info_text = FONT_SMALL.render(f"Шагов: {step_count} | Алгоритм: {'RR' if algorithm == 'rr' else 'Priority'}", True, TEXT_COL)
+            screen.blit(info_text, (30, HEIGHT - 50))
+            if draw_button("Выход", WIDTH - 100, 20, 80, 40):
+                running = False
+
+        # ------------------ СЦЕНА 3: ОБРАБОТКА ПРЕРЫВАНИЯ (МОДАЛЬНОЕ ОКНО) ------------------
+        elif scene == 3:
+            immediate = modal_interrupt(scheduler)
+            if immediate:
+                if scheduler.current:
+                    old = scheduler.current.priority
+                    scheduler.current.priority = max(1, scheduler.current.priority - 2)
+                    scheduler.interrupts_handled += 1
+                    message = f"✅ Прерывание обработано! Приоритет T{scheduler.current.tid} повышен с {old} до {scheduler.current.priority}"
+                else:
+                    message = "Прерывание, но нет активного потока"
+            else:
+                message = "⏸ Прерывание отложено. Эффективность снижена."
+            interrupt_available = True
+            scene = 2
+
+        # ------------------ СЦЕНА 4: ФИНАЛЬНАЯ СТАТИСТИКА ------------------
+        elif scene == 4:
+            if scheduler.interrupts_handled > 0:
+                grade = "A (Отлично)"
+                grade_color = (100, 255, 100)
+            elif step_count < 10:
+                grade = "B (Хорошо)"
+                grade_color = (200, 255, 100)
+            elif step_count < 20:
+                grade = "C (Удовлетворительно)"
+                grade_color = (255, 200, 100)
+            else:
+                grade = "D (Низкая эффективность)"
+                grade_color = (255, 120, 120)
+
+            stats = [
+                "📊 Статистика игры",
+                f"Алгоритм: {'Round Robin' if algorithm == 'rr' else 'Приоритетное (вытеснение)'}",
+                f"Всего шагов (квантов): {step_count}",
+                f"Прерываний обработано немедленно: {scheduler.interrupts_handled}",
+                f"Потоков завершено: {next_tid - 1}",
+                f"Оценка: {grade}"
+            ]
+            y = 180
+            for line in stats:
+                if "Оценка:" in line:
+                    txt = FONT.render(line, True, grade_color)
+                else:
+                    txt = FONT.render(line, True, TEXT_COL)
+                screen.blit(txt, (WIDTH // 2 - txt.get_width() // 2, y))
+                y += 55
+
+            if draw_button("🔄 Сыграть снова", WIDTH // 2 - 180, HEIGHT - 140, 160, 55):
+                scene = 0
+                algorithm = None
+                scheduler = None
+                threads_added = []
+                next_tid = 1
+                step_count = 0
+                interrupt_available = False
+            if draw_button("❌ Выход", WIDTH // 2 + 20, HEIGHT - 140, 160, 55):
+                running = False
+
+        pygame.display.flip()
+        clock.tick(30)
+
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                running = False
+
+    pygame.quit()
+    sys.exit()
+
+if __name__ == "__main__":
+    main()