Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
курсач.docx
Скачиваний:
17
Добавлен:
25.03.2018
Размер:
327.58 Кб
Скачать

Практична частина

Код проекту:

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Drawing;

using System.IO;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

using System.Windows.Forms;

namespace FilesReplicator

{

public partial class MainForm : Form

{

string SourcePath

{

get

{

return sourceBox.Text;

}

}

string TargetPath

{

get

{

return targetBox.Text;

}

}

int NumberFiles

{

get

{

if (Files.Count >= 0) return Files.Count;

else if (string.IsNullOrWhiteSpace(SourcePath))

return GetFilesInDirectories(new DirectoryInfo(SourcePath)).Count;

else return -1;

}

}

int NumberDirectories

{

get

{

if (Directories.Count >= 0) return Directories.Count;

else if (string.IsNullOrWhiteSpace(SourcePath))

return GetDirectories(new DirectoryInfo(SourcePath)).Count;

else return -1;

}

}

double SizeFiles

{

get

{

if (Files.Count > 0)

{

double size = 0;

foreach (var file in Files)

size += file.Length;

return size;

}

else return 0;

}

}

Semaphore syncer, timerSyncer;

List<DirectoryInfo> Directories;

List<FileInfo> Files;

bool isShowed = false; // флаг, сообщающий, выводилось ли сообщение с информацией

bool isStarted = false; // флаг, сообщающий о запуске копирования

double CopiedSize = 0;

int MaxThreads;

Stopwatch timer;

int NeededThreads;

Queue<FileInfo> FilesQueue; //Представляет коллекцию объектов, основанную на принципе "первым вошёл — первым вышел".

public MainForm()

{

InitializeComponent();

threadsNumberBox.Maximum = Environment.ProcessorCount * 4;

threadsNumberBox.Value = Environment.ProcessorCount;

syncer = new Semaphore(MaxThreads, MaxThreads + 1);

timerSyncer = new Semaphore(1, 1);

timer = new Stopwatch();

Files = new List<FileInfo>();

Directories = new List<DirectoryInfo>();

}

// Получает список всех папок в указанной директории

public List<DirectoryInfo> GetDirectories(DirectoryInfo source)

{

return source.GetDirectories("*", SearchOption.AllDirectories).ToList();

}

// Получает список всех файлов в указанной директории

public List<FileInfo> GetFilesInDirectories(DirectoryInfo sourceDirectory)

{

return sourceDirectory.GetFiles("*.*", SearchOption.AllDirectories).ToList();

}

// Копирует все папки в целевую директорию из исходной

public void CopyDirectories(string sourcePath, string targetPath)

{

if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath);

foreach (var directory in Directories)

Directory.CreateDirectory(DirectoryPathChanger(directory.FullName, sourcePath, targetPath));

}

// Выполняет замену расположения файла

string FilePathChanger(string fileName, string source, string target)

{

return fileName.Replace(source, target);

}

// Выполняет замену расположения папки

string DirectoryPathChanger(string directoryName, string source, string target)

{

return directoryName.Replace(source, target);

}

private void targetButton_Click(object sender, EventArgs e)

{

using (var opener = new FolderBrowserDialog())

{

try

{

opener.ShowNewFolderButton = true;

opener.RootFolder = Environment.SpecialFolder.MyComputer;

opener.Description = "Целевая директория для копирования";

if (opener.ShowDialog() == DialogResult.Cancel) return;

else if (opener.SelectedPath == SourcePath)

throw new ArgumentException("Целевой путь не может совпадать с исходным.");

targetBox.Text = opener.SelectedPath;

}

catch (Exception ex)

{

MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

}

private void sourceButton_Click(object sender, EventArgs e)

{

using (var opener = new FolderBrowserDialog())

{

try

{

opener.ShowNewFolderButton = false;

opener.RootFolder = Environment.SpecialFolder.MyComputer;

opener.Description = "Исходная директория для копирования";

if (opener.ShowDialog() == DialogResult.Cancel) return;

sourceBox.Text = opener.SelectedPath;

}

catch (Exception ex)

{

MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

}

private void threadsNumberBox_ValueChanged(object sender, EventArgs e)

{

MaxThreads = Convert.ToInt32(threadsNumberBox.Value);

}

//изменение текста заданого пути

private void sourceBox_TextChanged(object sender, EventArgs e)

{

if (!string.IsNullOrWhiteSpace(SourcePath) &&

!string.IsNullOrWhiteSpace(TargetPath) &&

Directory.Exists(SourcePath)) startButton.Enabled = true;

else startButton.Enabled = false;

if (!Directory.Exists(SourcePath) ||

string.IsNullOrWhiteSpace(SourcePath)) sourceBox.BackColor = Color.Red;

else

{

sourceBox.BackColor = Color.White;

try

{

if (NumberFiles > 0) Files.Clear();

if (NumberDirectories > 0) Directories.Clear();

Files = GetFilesInDirectories(new DirectoryInfo(SourcePath));

Directories = GetDirectories(new DirectoryInfo(SourcePath));

}

catch (Exception ex)

{

MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

finally

{

PartsLoaderCalculator();

}

}

}

// расчет ресурсов в исходной папке

void PartsLoaderCalculator()

{

if (NumberFiles > 0)

{

if (NumberFiles % 10 == 1

&& (NumberFiles > 20 || NumberFiles < 10))

filesLabel.Text = NumberFiles.ToString() + " файл";

else if (NumberFiles % 10 > 1 && NumberFiles % 10 < 5 &&

(NumberFiles > 20 || NumberFiles < 10))

filesLabel.Text = NumberFiles.ToString() + " файла";

else filesLabel.Text = NumberFiles.ToString() + " файлов";

if (SizeFiles < 1024) sizeLabel.Text = SizeFiles.ToString() + " Б";

else if (SizeFiles < 1024 * 1024)

sizeLabel.Text = (SizeFiles / 1024).ToString("F2") + " кБ";

else if (SizeFiles < 1024 * 1024 * 1024)

sizeLabel.Text = (SizeFiles / 1024 / 1024).ToString("F2") + " МБ";

else if (SizeFiles >= 1024 * 1024 * 1024)

sizeLabel.Text = (SizeFiles / 1024 / 1024 / 1024).ToString("F2") + " ГБ";

CopiedSize = 0;

}

else filesLabel.Text = "Нет файлов";

if (NumberDirectories > 0)

{

if (NumberDirectories % 10 == 1

&& (NumberDirectories > 20 || NumberDirectories < 10))

dirsLabel.Text = NumberDirectories.ToString() + " папка";

else if (NumberDirectories % 10 > 1 && NumberDirectories % 10 < 5 &&

(NumberDirectories > 20 || NumberDirectories < 10))

dirsLabel.Text = NumberDirectories.ToString() + " папки";

else dirsLabel.Text = NumberDirectories.ToString() + " папок";

}

else dirsLabel.Text = "Нет папок";

Invalidate();

}

// изменении текста в поле ввода целевого пути

private void targetBox_TextChanged(object sender, EventArgs e)

{

if (!string.IsNullOrWhiteSpace(SourcePath) &&

Directory.Exists(SourcePath) &&

!string.IsNullOrWhiteSpace(TargetPath)) startButton.Enabled = true;

else startButton.Enabled = false;

if (string.IsNullOrWhiteSpace(TargetPath)) targetBox.BackColor = Color.Red;

else targetBox.BackColor = Color.White;

}

// нажатие кнопки "Начать"

private void startButton_Click(object sender, EventArgs e)

{

isShowed = false;

isStarted = true;

startButton.Enabled = false;

if (timer.ElapsedMilliseconds > 0) timer.Reset();

timer.Start();

CopyDirectories(SourcePath, TargetPath);

timer.Stop();

CopiedSize = 0;

Invalidate();

//ручные потоки

// преобразовываем список файлов в очередь

FilesQueue = new Queue<FileInfo>(Files);

//определяем кол-во нужных потоков

if (NumberFiles < MaxThreads) NeededThreads = NumberFiles;

else NeededThreads = MaxThreads;

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

new Thread(FileReplicatorLoop) { IsBackground = true, Name = "Thread " + (i + 1) }.Start();

// для копирования файлов с ручным созданием потоков

void FileReplicatorLoop()

{

FileInfo file;

while (true)

{

if (FilesQueue.Count > 0)

{

try

{

timerSyncer.WaitOne();

file = FilesQueue.Dequeue();

timer.Start();

file.CopyTo(FilePathChanger(file.FullName, SourcePath, TargetPath), true);

timer.Stop();

CopiedSize += file.Length;

timerSyncer.Release();

}

catch (InvalidOperationException)

{

// если в очереди не осталось файлов

// отменяем блокировку таймера и счетчика

timerSyncer.Release();

Thread.CurrentThread.Abort();

break;

}

}

else

{

Thread.CurrentThread.Abort();

break;

}

Invalidate();

}

}

// метод, обрабатывающий вызов Invalidate() из других потоков

private void MainForm_Paint(object sender, PaintEventArgs e)

{

// блокируем доступ к счетчикам объёма скопированных файлов,

// объёма всех файлов и ресурсам таймера

syncer.WaitOne();

// если сообщение не выводилось и в список добавлены файлы

if (SizeFiles != 0 && CopiedSize == SizeFiles && !isShowed)

{

isShowed = true;

isStarted = false;

MessageBox.Show(string.Format("Затрачено времени: {0:#,0} мс\n" +

"Средняя скорость передачи: {1:F2} МБ/с", timer.ElapsedMilliseconds,

(SizeFiles / timer.ElapsedMilliseconds) * 1000 / 1024 / 1024),

"Копирование завершено", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

if (!isStarted)

{

startButton.Text = "Начать";

startButton.Enabled = true;

}

else startButton.Text = string.Format("Копирование... {0:F1} %", CopiedSize / SizeFiles * 100);

// если скопированы все файлы

if (CopiedSize == SizeFiles && isStarted) isStarted = false;

syncer.Release();

}

}

}

Скріншоти роботи проекту

Рис.1. Вибір каталогу, з якого будуть копіюватися файли

Рис.2. Розрахована кількість папок та файлів у початковому каталозі

Рис.3. Процес копіювання файлів

Рис.4. Витрачений час на копіювання файлів

ДОСЛІДЖЕННЯ РОБОТИ ПРОГРАМИ

Для визначення оптимальної кількості потоків виміряємо витрачений час на копіювання файлів розміром від 197 Байт до 990 МБайт.

Таблиця 1

197 Байт

Кількість потоків

Час, мс

1

35

2

2

3

2

4

2

5

2

6

3

7

4

8

4

Рис.5. Графік залежності кількість потоків від часу для 197 Байт.

Таблиця 2

1.45 МБайт

Кількість потоків

Час, мс

1

1137

2

724

3

61

4

38

5

78

6

83

7

381

8

796

Рис.6. Графік залежності кількість потоків від часу для 1.45 МБайт.

Таблиця 3

7.48 МБайт

Кількість потоків

Час, мс

1

13600

2

2070

3

1290

4

1293

5

1671

6

1546

7

1601

8

2911

Рис.7. Графік залежності кількість потоків від часу для 7.48 МБайт.

Таблиця 4

990 МБайт

Кількість потоків

Час, мс

1

93250

2

84270

3

84260

4

76769

5

79561

6

74281

7

72288

8

72980

Рис.8. Графік залежності кількість потоків від часу для 990 МБайт.

ВИСНОВОК

Для копіювання невеликої кількості файлів (1 Байт – 600 МБайт) оптимально використовувати 3-6 потоків. Для більшої кількості файлів (від 600 МБайт) оптимально використовувати 6-8 потоків. За отриманими даними можна зробити висновок, що велика кількість потоків не завжди гарантує швидке виконання програми. Багатопоточність доцільно використовувати тоді, коли необхідно працювати з великою кількістю данних.