Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

отчет лаба 3 Гаранин

.docx
Скачиваний:
0
Добавлен:
11.02.2026
Размер:
233.04 Кб
Скачать

ФЕДЕРАЛЬНОЕ АГЕНСТВО ВОЗДУШНОГО ТРАНСПОРТА

(РОСАВИАЦИЯ)

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ

«МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ГРАЖДАНСКОЙ АВИАЦИИ» (МГТУ ГА)

Кафедра вычислительных машин, комплексов, сетей и систем.

Лабораторная работа защищена с оценкой ____________________

____________________

(подпись преподавателя, дата)

ЛАБОРАТОРНАЯ РАБОТА №3

по дисциплине «Методы визуального и параллельного программирования».

Тема: «Создание вложенных задач. Реализация вложенных задач на основе параллелизма.»

Выполнила студент группы ИС221

Магальник Екатерина Борисовна

Руководитель: Гаранин Сергей Александрович

МОСКВА – 2025

Цель работы:

Изучение механизмов создания и управления вложенными задачами в среде .NET с применением параллельного программирования. Освоение практических приёмов организации иерархии задач, привязки подзадач к родительским, а также реализация управления отменой выполнения с использованием токенов отмены.

Вариант 7.

Разработать консольное приложение на языке C# выполняющее фильтрацию элементов матрицы по заданному диапазону. Каждая строка матрицы должна обрабатываться в виде отдельной задачи (основная задача), а каждый элемент строки – в виде вложенной задачи (подзадачи).

Условия:

  1. Размер матрицы и диапазон фильтрации задается пользователем.

  2. Каждая строка – основная задача, каждый элемент – подзадача: если элемент вне диапазона – заменить на 0.

  3. Все подзадачи должны быть привязаны к своим родительским задачам (AttachedToParent).

  4. Необходимо реализовать корректное ожидание завершения всех задач.

  5. Вывести результат на экран.

  6. Добавить возможность отмены выполнения задач с использованием CancellationToken.

Ход работы:

Рис. 1. Алгоритм функции Task Main(string[] args).

Листинг:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

namespace MatrixFilterApp

{

class Program

{

static async Task Main(string[] args)

{

Console.WriteLine("--- Фильтрация матрицы с использованием задач ---");

// 1. Размер матрицы и диапазон фильтрации задается пользователем.

int rows = GetIntegerInput("Введите количество строк матрицы: ");

int cols = GetIntegerInput("Введите количество столбцов матрицы: ");

int filterMin = GetIntegerInput("Введите минимальное значение для фильтра: ");

int filterMax = GetIntegerInput("Введите максимальное значение для фильтра (>= мин): ", filterMin);

// Инициализация матрицы случайными значениями

int[,] matrix = new int[rows, cols];

Random random = new Random();

for (int i = 0; i < rows; i++)

{

for (int j = 0; j < cols; j++)

{

matrix[i, j] = random.Next(filterMin - 20, filterMax + 20); // Для разнообразия значений

}

}

Console.WriteLine("\nИсходная матрица:");

PrintMatrix(matrix);

// 6. Добавить возможность отмены выполнения задач с использованием CancellationToken.

CancellationTokenSource cts = new CancellationTokenSource();

CancellationToken token = cts.Token;

Console.WriteLine("\nНажмите 'C' для отмены операции фильтрации в любой момент.");

Console.WriteLine("Начинаю фильтрацию...");

// Запускаем задачу для отслеживания нажатия клавиши отмены

Task cancellationMonitor = Task.Run(() =>

{

while (!cts.IsCancellationRequested)

{

if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.C)

{

Console.WriteLine("\n!!! Получен запрос на отмену операции !!!");

cts.Cancel();

break;

}

Thread.Sleep(100); // Небольшая задержка, чтобы не нагружать CPU

}

});

List<Task> parentTasks = new List<Task>();

try

{

for (int i = 0; i < rows; i++)

{

if (token.IsCancellationRequested)

{

Console.WriteLine($"Отмена перед запуском задачи для строки {i}.");

token.ThrowIfCancellationRequested(); // Выбросит OperationCanceledException

}

// Копируем 'i' в локальную переменную для замыкания

int currentRow = i;

// 2. Каждая строка – основная задача

Task parentTask = Task.Factory.StartNew(() =>

{

Console.WriteLine($"[Основная задача] Начата обработка строки {currentRow} (ID: {Task.CurrentId})");

token.ThrowIfCancellationRequested(); // Проверка перед запуском подзадач

List<Task> childTasks = new List<Task>();

for (int j = 0; j < cols; j++)

{

if (token.IsCancellationRequested)

{

Console.WriteLine($"Отмена перед запуском подзадачи для элемента [{currentRow},{j}].");

token.ThrowIfCancellationRequested();

}

int currentCol = j;

// 2. каждый элемент – подзадача

// 3. Все подзадачи должны быть привязаны к своим родительским задачам (AttachedToParent).

Task childTask = Task.Factory.StartNew(() =>

{

token.ThrowIfCancellationRequested(); // Проверяем токен в начале подзадачи

// Имитация некоторой работы

Thread.Sleep(random.Next(10, 50));

int element = matrix[currentRow, currentCol];

if (element < filterMin || element > filterMax)

{

matrix[currentRow, currentCol] = 0;

// Console.WriteLine($" [Подзадача] Элемент [{currentRow},{currentCol}] ({element}) заменен на 0 (ID: {Task.CurrentId}, ParentID: {Task.CurrentId.HasValue ? Task.Factory.GetCurrentTask().Parent?.Id : null})");

}

else

{

// Console.WriteLine($" [Подзадача] Элемент [{currentRow},{currentCol}] ({element}) в диапазоне (ID: {Task.CurrentId}, ParentID: {Task.CurrentId.HasValue ? Task.Factory.GetCurrentTask().Parent?.Id : null})");

}

token.ThrowIfCancellationRequested(); // Проверка после работы (на всякий случай)

}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); // Передаем токен и опцию

childTasks.Add(childTask);

}

// Родительская задача неявно ждет завершения всех AttachedToParent подзадач

// Task.WaitAll(childTasks.ToArray(), token); // Можно и так, но не обязательно для AttachedToParent

Console.WriteLine($"[Основная задача] Завершена обработка строки {currentRow} (ID: {Task.CurrentId})");

}, token); // Передаем токен родительской задаче

parentTasks.Add(parentTask);

}

// 4. Необходимо реализовать корректное ожидание завершения всех задач.

// Ожидаем завершения всех родительских задач.

// Поскольку дочерние задачи AttachedToParent, ожидание родительских задач

// гарантирует завершение и дочерних.

await Task.WhenAll(parentTasks); // Асинхронное ожидание

Console.WriteLine("\nФильтрация успешно завершена.");

}

catch (OperationCanceledException)

{

Console.WriteLine("\nОперация фильтрации была отменена пользователем.");

// Можно добавить логику очистки или обработки частично измененной матрицы, если нужно.

}

catch (Exception ex)

{

Console.WriteLine($"\nПроизошла ошибка во время фильтрации: {ex.Message}");

}

finally

{

if (!cts.IsCancellationRequested) // Если не было отмены, "отменяем" монитор, чтобы он завершился

{

cts.Cancel();

}

await cancellationMonitor; // Убедимся, что задача мониторинга завершилась

cts.Dispose();

}

Console.WriteLine("\nОтфильтрованная матрица:");

PrintMatrix(matrix);

Console.WriteLine("\nНажмите любую клавишу для выхода.");

Console.ReadKey();

}

static int GetIntegerInput(string prompt, int? minValue = null)

{

int value;

while (true)

{

Console.Write(prompt);

if (int.TryParse(Console.ReadLine(), out value) && (minValue == null || value >= minValue))

{

return value;

}

Console.WriteLine($"Некорректный ввод. Пожалуйста, введите целое число" + (minValue.HasValue ? $" не менее {minValue.Value}" : "") + ".");

}

}

static void PrintMatrix(int[,] matrix)

{

int rows = matrix.GetLength(0);

int cols = matrix.GetLength(1);

for (int i = 0; i < rows; i++)

{

for (int j = 0; j < cols; j++)

{

Console.Write($"{matrix[i, j],5}"); // Форматируем вывод для аккуратности

}

Console.WriteLine();

}

}

}

}

Результат работы программы:

Рис. 2. Результат запуска программы.

Вывод:

1. Успешная реализация требований: программа полностью соответствует поставленным задачам:

⦁ Пользовательский ввод размеров матрицы и диапазона фильтрации реализован.

⦁ Фильтрация элементов матрицы выполняется с заменой элементов вне диапазона на 0.

⦁ Обработка строк матрицы организована как основные задачи (Task).

⦁ Обработка каждого элемента строки организована как вложенная подзадача (Task) с использованием опции TaskCreationOptions.AttachedToParent, что обеспечивает их привязку к родительской задаче.

⦁ Реализовано корректное ожидание завершения всех основных задач (и, как следствие, всех привязанных к ним подзадач) с помощью await Task.WhenAll().

⦁ Исходная и отфильтрованная матрицы выводятся на экран для верификации результата.

⦁ Реализован механизм отмены операций с использованием CancellationTokenSource и CancellationToken, позволяющий пользователю прервать процесс фильтрации.

2. Демонстрация возможностей TPL (Task Parallel Library):

⦁ Эффективно использованы задачи для распараллеливания обработки данных матрицы, что потенциально может ускорить выполнение на многоядерных процессорах, особенно для матриц большого размера.

⦁ Показано создание иерархии задач (родительские и дочерние), где родительская задача не завершается до завершения всех ее присоединенных дочерних задач. Это упрощает управление сложными параллельными операциями.

⦁ Продемонстрировано использование async/await для асинхронного ожидания завершения задач, что предотвращает блокировку основного потока и позволяет реализовать отзывчивый механизм отмены (например, мониторинг нажатия клавиши).

3. Реализация отмены операций:

⦁ Механизм отмены через CancellationToken является важным аспектом для длительных операций. Он позволяет пользователю контролировать выполнение программы и корректно прерывать задачи, освобождая ресурсы и предотвращая нежелательные вычисления.

⦁ Проверка токена отмены (token.ThrowIfCancellationRequested() или token.IsCancellationRequested) в ключевых точках выполнения задач (в начале, перед запуском новых подзадач, иногда после ресурсоемких операций) обеспечивает своевременную реакцию на запрос отмены.

4. Практическое применение иерархии задач:

⦁ Опция AttachedToParent удобна, когда выполнение родительской задачи логически зависит от полного завершения набора дочерних операций. Это избавляет от необходимости вручную ожидать каждую дочернюю задачу внутри родительской, если такое поведение является желаемым по умолчанию.

5. Потенциальные соображения и улучшения (за рамками текущей задачи):

⦁ Накладные расходы: для очень маленьких матриц или очень простых операций над элементами накладные расходы на создание и управление большим количеством задач могут превысить выгоду от распараллеливания. В таких случаях последовательная обработка может быть эффективнее.

⦁ Гранулярность задач: Выбор того, что является "основной" и "вложенной" задачей (гранулярность), влияет на производительность. Слишком мелкие задачи приводят к большим накладным расходам, слишком крупные — к недостаточному распараллеливанию. Выбранный подход (строка — основная, элемент — подзадача) является одним из логичных вариантов для данной задачи.

⦁ Обработка исключений: Хотя OperationCanceledException обрабатывается, в более сложных сценариях может потребоваться более детальная обработка других исключений, возникающих в задачах (которые, благодаря AttachedToParent, будут распространяться на родительскую задачу).

В целом, разработанное консольное приложение успешно демонстрирует ключевые аспекты многозадачного программирования в C# с использованием TPL, включая создание иерархии задач, их корректное асинхронное ожидание и реализацию надежного механизма отмены операций. Это является хорошей основой для понимания и применения данных концепций в более сложных системах.