Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебники 60141.doc
Скачиваний:
7
Добавлен:
01.05.2022
Размер:
1.2 Mб
Скачать

Лабораторная работа № 3. Методы синхронизации для управления несколькими потоками

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

Теоретический материал к выполнению заданий лабораторной работы представлен в электронном учебнике в ПМК по дисциплине.

Задание 1. Имеется массив целых чисел, который нужно инициализировать восходящими значениями. Сначала надо "пройти" по массиву и установить значения от 1 до 128, а затем переинициализировать этот массив значениями от 128 до 255. После этого необходимо отобразить финальный поток в окне списка.

Составить программу для данной задачи. Выполнение инициализаций реализовать в двух отдельных потоках.

Ход выполнения задания:

1.Создать Windows приложение в среде Microsoft Visual C# 2008 Framework 3.5

2.Добавить на форму объект Button1, организовать выход из приложения, используя метод Close();

3.В тексте программы после метода, который инициализирует компоненты формы, добавить описание статистического одномерного массива, размерностью 128 элементов:

static int[] mas= new int[128];

4. Добавить метод, инициализирующий массив заданными в задании значениями:

public void Initmassiv(int n) // 1 или 128

{

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

{

massiv[i] = i + n;

}

}

5. Добавить на форму объект Button2 для проведения инициализации массива и объект TextBox1 для выдачи результатов работы

6. По щелчку на объект Button2 организовать инициализацию массива сначала одними значениями, затем другими (по заданию) и выдать результат инициализации:

Initmassiv(1);

Initmassiv(128);

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

textBox1.Text += massiv[i] + " ";

7.Запустить программу на выполнение. Пример экранной формы программы инициализации массива от 0 до 128 и от 128 до 255 представлен на рис. 3.

Рис. 3. Экранная форма программы инициализации массива

8. Теперь надо создать два потока, каждый из которых будет инициализировать массив massiv: первый поток – от 0 до 128, второй – от 128 до 255. Для этого выполнить следующие изменения программы:

- добавить в перечень используемых модулей дополнительный

using System.Threading;

- закомментировать вызовы инициализации массива в событии объекта Button2 и вместо этих операторов добавить операторы работы с многопоточным приложением;

- создать два потока:

Thread pot1 = new Thread(new ParameterizedThreadStart(Initmassiv));

Thread pot2 = new Thread(new ParameterizedThreadStart(Initmassiv));

- запустить два потока:

pot1.Start(0);

pot2.Start(128);

- ждать завершения работы потоков:

pot1.Join();

pot2.Join();

- изменить метод, инициализирующий массив, следующим образом: тип аргумента n изменить на object, в теле метода добавить оператор преобразования типа переменной n в целочисленную, добавить оператор задержки потока. В результате изменений код метода имеет следующий вид:

public void Initmassiv(object n) // 1 или 128

{

int n1 = (int)n;

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

{

massiv[i] = i + n1;

Thread.Sleep(10);

}

}

9. Запустить программу на выполнение. Пример экранной формы программы инициализации массива двумя потоками представлен на рис. 4. Обратите внимание, что выдача результатов работы двух потоков демонстрирует получение некорректного результата работы программы, т.е. необходимо осуществлять синхронизацию потоков.

Рис. 4. Экранная форма программы инициализации массива двумя потоками

Задание 2. Составить программу для задачи Задания 1. Выполнение инициализаций реализовать в двух отдельных потоках с синхронизацией инициализирующих массив потоков с помощью различных методов синхронизации потоков (не менее трех).

Представлен пример выполнения задания для таких методов синхронизации, как монитор, мьютекс и семафор.

Ход выполнения задания для реализации монитора:

1.Скопировать проект задания 1 и произвести в нем следующие изменения.

2.В коде программы после описания массива добавить описание переменной syn - объекта синхронизации для реализации монитора:

Object syn;

3.В метод Initmassiv добавить операторы, первый из которых установить в начале метода (он включит блокировку и до завершения выполнения метода не пустит другой поток), второй оператор – последний метода инициализации массива (он снимет блокировку и инициализация массива будет доступна другому потоку):

Monitor.Enter(syn); - включение блокировки

Monitor.Exit(syn); - снятие блокировки

4. В объекте Button2 изменить свойство Text на «Монитор».

5.В событии объекта Button2 добавить оператор для создания объекта синхронизации до описания потоков:

syn = new Object();

6. Запустить программу на выполнение. Пример экранной формы программы синхронизации потоков методом монитора представлен на рис. 5.

Рис. 5. Синхронизация потоков методом монитора

Ход выполнения задания для реализации мьютекса:

1.Скопировать проект задания 1 и произвести в нем следующие изменения.

2.В коде программы после описания массива добавить оператор для создания мьютекса:

Mutex m = new Mutex();

3.В метод Initmassiv добавить операторы, первый из которых установить в начале метода (он включит блокировку и до завершения выполнения метода не пустит другой поток), второй оператор – последний метода инициализации массива (он снимет блокировку и инициализация массива будет доступна другому потоку):

m.WaitOne(); - включение блокировки

m.ReleaseMutex(); - снятие блокировки

4. В объекте Button2 изменить свойство Text на «Мьютекс».

5.Запустить программу на выполнение. Экранная форма программы синхронизации потоков методом мьютекса аналогична форме на рис. 5.

Ход выполнения задания для реализации семафора:

1.Скопировать проект задания 1 и произвести в нем следующие изменения.

2.В коде программы после описания массива добавить оператор для создания семафора:

Semaphore s = new Semaphore(1, 1);

3.В метод Initmassiv добавить операторы, первый из которых установить в начале метода (он включит блокировку и до завершения выполнения метода не пустит другой поток), второй оператор – последний метода инициализации массива (он снимет блокировку и инициализация массива будет доступна другому потоку):

s.WaitOne(); - включение блокировки

s.Release(); - снятие блокировки

4. В объекте Button2 изменить свойство Text на «Семафор».

5.Запустить программу на выполнение. Экранная форма программы синхронизации потоков методом семафора аналогична форме на рис. 5.

Листинг программы выполнения задания с синхронизацией потоков с помощью критических разделов:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.Threading;

namespace лаб3

{

public partial class Form1 : Form

{

//объявление массива

public static int[] massiv = new int[128] ;

public Form1()

{

InitializeComponent();

}

public void fun1(object param)

{

//блокировка метода. т.е. метод может выполняться одновременно только одним потоком

lock(this)

{

//приведение к типу int параметра

int n = (int)param;

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

{

//задание значения для элемента массива с индексом i

massiv[i] = i + 1 + n;

//задержка потока

Thread.Sleep(1);

}

}

}

private void button1_Click(object sender, EventArgs e)

{

//создание двух потоков

Thread t1 = new Thread(new ParameterizedThreadStart(fun1));

Thread t2 = new Thread(new ParameterizedThreadStart(fun1));

//запуск потоков

t1.Start(0);

//"усыпление" текущего потока

Thread.Sleep(0);

t2.Start(127);

//приостановка потоков

t1.Join();

t2.Join();

//вывод элементов массива на экран

richTextBox1.Text = "";

for (int i = 0; i < massiv.Length;i++ )

{

richTextBox1.Text += massiv[i].ToString() + "\n";

}

}

}

}

Экранная форма программы до и после синхронизации представлена на рис. 6.

Рис. 6. Результаты работы программы до и после

синхронизации

Пример разработки программы для расчета решета Эратосфена, чисел Фибоначчи, факториала с синхронизацией потоков:

1) Добавим элементы textBox1, textBox2, textBox3, listBox1, listBox2, listBox3, button1, button2, button3, button4, button5, label1, label2, label3, label4, trackBar1, trackBar2, trackBar3, trackBar4, richTextBox1 на форму согласно рис. 7.

2)Теперь подключим необходимые библиотеки:

using System.IO;

using System.Threading;

Рис. 7. Расположение элементов на форме

3)Объявим необходимые переменные в классе Form1

private ThreadStart t1; //Делегат; представляет метод, выполняющийся в первом потоке

private ThreadStart t2; //Делегат; представляет метод, выполняющийся во втором потоке

private ThreadStart t3; //Делегат; представляет метод, выполняющийся в третьем потоке

private Thread Thread_1; //Первый поток

private Thread Thread_2; //Второй поток

private Thread Thread_3; //Третий поток

private Semaphore s; //Семафор, служащий для ограничения //числа потоков, которые обращаются к richTextBox1

private bool first; //Переменная, хранящая информацию //первый ли запуск потоков

private int pause; //Переменная, хранящая длительность //паузы

4)Изменим стандартный конструктор формы

public Form1()

{

InitializeComponent(); //Инициализация элементов формы

s = new Semaphore(1, 1); //Создание семафора с начальным и //максимальным количеством запросов для семафора, которое //может быть обеспечено одновременно

int n1 = 1000; //Переменная для расчета решета Эратосфена

textBox1.Text = n1.ToString();

int n2 = 100; //Переменная для расчета чисел Фибоначчи

textBox2.Text = n2.ToString();

int n3 = 10; //Переменная для расчета факториала

textBox3.Text = n3.ToString();

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; //Отменяем проверку на доступ к объектам, //созданным из другого потока

first = true; //Указываем первый запуск

button2.Text = "Поток №1 не запущен"; //Изменяем текст //кнопки button2

button3.Text = "Поток №2 не запущен"; //Изменяем текст //кнопки button3

button4.Text = "Поток №3 не запущен"; //Изменяем текст //кнопки button4

button2.Enabled = false; //Блокируем кнопку button2

button3.Enabled = false; //Блокируем кнопку button3

button4.Enabled = false; //Блокируем кнопку button4

pause = 100; //Указываем длительность паузы

trackBar1.Value = 3; //Указываем значение для trackbar1, //изменяющий приоритет первого потока

trackBar2.Value = 3; //Указываем значение для trackbar2, //изменяющий приоритет второго потока

trackBar3.Value = 3; //Указываем значение для trackbar3, //изменяющий приоритет третьего потока

trackBar4.Value = 1; //Указываем значение для trackbar4, //изменяющий длительность паузы

}

5)Добавим функцию f1 для расчета решета Эратосфена

private void f1(int n1)

{

listBox1.Items.Clear();

List<ulong> arr_1 = new List<ulong>();

ulong i = 2;

while (arr_1.Count < n1)

{

if (arr_1.Count == 0)

{

arr_1.Add(2);

listBox1.Items.Add(i);

s.WaitOne(); //Блокируем текущий поток до освобо- //ждения семафора

writeTextBox(1); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на за- //данное количество миллисекунд

i = 3;

continue;

}

bool temp = false;

for (int k = 0; k < arr_1.Count; k++)

{

if (i % arr_1[k] == 0)

{

temp = true;

}

}

if (!temp)

{

arr_1.Add(i);

listBox1.Items.Add(i);

s.WaitOne(); //Блокируем текущий поток до освобо- //ждения семафора

writeTextBox(1); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на

//заданное количество миллисекунд

}

i++;

}

button2.Enabled = false;

button2.Text = "Поток №1 не запущен";

}

6)Добавим функцию f2 для расчета чисел Фибоначчи

private void f2(int n2)

{

listBox2.Items.Clear();

List<ulong> arr_2 = new List<ulong>();

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

{

if (arr_2.Count == 0 || arr_2.Count == 1)

{

arr_2.Add(1);

listBox2.Items.Add(i);

s.WaitOne(); //Блокируем текущий поток до

//освобождения семафора

writeTextBox(2); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на

//заданное количество миллисекунд

continue;

}

else

{

arr_2.Add(arr_2[arr_2.Count - 1] + arr_2[arr_2.Count - 2]);

listBox2.Items.Add(i);

s.WaitOne(); //Блокируем текущий поток до

//освобождения семафора

writeTextBox(2); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на

//заданное количество миллисекунд

}

}

button3.Enabled = false;

button3.Text = "Поток №2 не запущен";

}

7)Добавим функцию f3 для расчета факториала

private void f3(int n3)

{

listBox3.Items.Clear();

List<ulong> arr_3 = new List<ulong>();

for (int i = 1; i <= n3; i++)

{

if (arr_3.Count == 0)

{

arr_3.Add((ulong)i);

listBox3.Items.Add(arr_3[i-1]);

s.WaitOne(); //Блокируем текущий поток до

//освобождения семафора

writeTextBox(3); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на

// заданное количество миллисекунд

}

else

{

arr_3.Add(arr_3[arr_3.Count - 1] * (ulong)i);

listBox3.Items.Add(arr_3[i-1]);

s.WaitOne(); //Блокируем текущий поток до

//освобождения семафора

writeTextBox(3); //Записываем в richTextBox1 номер //потока

s.Release(1); //Освобождаем семафор

Thread.Sleep(pause); //Блокируем текущий поток на

//заданное количество миллисекунд

}

}

button4.Enabled = false;

button4.Text = "Поток №3 не запущен";

}

8)Добавим функцию writeTextBox для вывода номера потока в richTextBox1

public void writeTextBox(int r)

{

switch (r)

{

case 1: richTextBox1.SelectionColor = Color.Yellow; //Задаем цвет для первого потока

break;

case 2: richTextBox1.SelectionColor = Color.Red; //Задаем //цвет для второго потока

break;

case 3: richTextBox1.SelectionColor = Color.Blue; //Задаем //цвет для третьего потока

break;

}

richTextBox1.AppendText(r + "\n"); //Выводим номер потока //в richTextBox1

}

9)Добавим функцию button1_Click, обрабатывающую событие нажатия на кнопку button1

private void button1_Click(object sender, EventArgs e)

{

richTextBox1.Text = "";

int n1;

try

{

n1 = Int32.Parse(textBox1.Text); //Считываем параметр для //расчета решета Эрастовена

}

catch

{

n1 = 0;

textBox1.Text = n1.ToString();

}

int n2;

try

{

n2 = Int32.Parse(textBox2.Text); //Считываем параметр для //расчета чисел Фибоначчи

}

catch

{

n2 = 0;

textBox2.Text = n2.ToString();

}

int n3;

try

{

n3 = Int32.Parse(textBox3.Text); //Считываем параметр для //расчета факториала

}

catch

{

n3 = 0;

textBox3.Text = n3.ToString();

}

if (!first) //Если не первый запуск потоков

{

s.WaitOne(); //Ждем свободный семафор

Thread_1.Abort(); //Завершаем поток

Thread_2.Abort(); //Завершаем поток

Thread_3.Abort(); //Завершаем поток

}

t1 = delegate { f1(n1); }; //Связываем делегат t1 с функцией f1

t2 = delegate { f2(n2); }; //Связываем делегат t2 с функцией f2

t3 = delegate { f3(n3); }; //Связываем делегат t3 с функцией f3

s = new Semaphore(1, 1); //Создание семафора с начальным и //максимальным количеством запросов для семафора, которое //может быть обеспечено одновременно

Thread_1 = new Thread(t1); //Создаем первый поток

Thread_2 = new Thread(t2); //Создаем второй поток

Thread_3 = new Thread(t3); //Создаем третий поток

Thread_1.Name = "Thread1"; //Указываем имя для потока

Thread_2.Name = "Thread2"; //Указываем имя для потока

Thread_3.Name = "Thread3"; //Указываем имя для потока

Thread_1.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar1.Value - 1)); //Устанавливаем приоритет для потока

Thread_2.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar2.Value - 1)); //Устанавливаем приоритет для потока

Thread_3.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar3.Value - 1)); //Устанавливаем приоритет для потока

Thread_1.Start(); //Запускаем поток

Thread_2.Start(); //Запускаем поток

Thread_3.Start(); //Запускаем поток

button2.Text = "Остановить поток №1";

button3.Text = "Остановить поток №2";

button4.Text = "Остановить поток №3";

button2.Enabled = Thread_1.IsAlive;

button3.Enabled = Thread_2.IsAlive;

button4.Enabled = Thread_3.IsAlive;

}

10)Добавим функцию button2_Click, обрабатывающую событие нажатия на кнопку button2

private void button2_Click(object sender, EventArgs e)

{

if (!Thread_1.IsAlive) //Если первый поток не запущен

{

s.WaitOne(); //Ждем освобождения семафора

Thread_1 = new Thread(t1);

Thread_1.Name = "Thread1";

Thread_1.Start(); //Запускаем поток

Thread_1.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar1.Value - 1));

button2.Text = "Остановить поток №1";

s.Release(1); //Освобождаем семафор

}

else //иначе

{

s.WaitOne(); //Ждем освобождения семафора

Thread_1.Abort(); //Завершаем поток

button2.Text = "Начать заново поток №1";

s.Release(1); //Освобождаем семафор

}

}

11)Добавим функцию button3_Click, обрабатывающую событие нажатия на кнопку button3

private void button3_Click(object sender, EventArgs e)

{

if (!Thread_2.IsAlive) //Если второй поток не запущен

{

s.WaitOne(); //Ждем освобождения семафора

Thread_2 = new Thread(t2);

Thread_2.Name = "Thread1";

Thread_2.Start(); //Запускаем поток

Thread_2.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar2.Value - 1));

button3.Text = "Остановить поток №2";

s.Release(1); //Освобождаем семафор

}

else //иначе

{

s.WaitOne(); //Ждем освобождения семафора

Thread_2.Abort(); //Завершаем поток

button3.Text = "Начать заново поток №2";

s.Release(1); //Освобождаем семафор

}

}

12)Добавим функцию button4_Click, обрабатывающую событие нажатия на кнопку button4

private void button4_Click(object sender, EventArgs e)

{

if (!Thread_3.IsAlive) //Если третий поток не запущен

{

s.WaitOne(); //Ждем освобождения семафора

Thread_3 = new Thread(t3);

Thread_3.Name = "Thread3";

Thread_3.Start(); //Запускаем поток

Thread_3.Priority = (ThreadPriority)Enum.Parse(typeof

(ThreadPriority), Convert.ToString(trackBar3.Value - 1));

button4.Text = "Остановить поток №3";

s.Release(1); //Освобождаем семафор

}

else //иначе

{

s.WaitOne(); //Ждем освобождения семафора

Thread_3.Abort(); //Завершаем поток

button4.Text = "Начать заново поток №3";

s.Release(1); //Освобождаем семафор

}

}

13)Добавим функцию button5_Click, обрабатывающую событие нажатия на кнопку button5

private void button5_Click(object sender, EventArgs e)

{

s.WaitOne(); //Ждем освобождения семафора

Environment.Exit(1); //Завершаем приложение

}

14)Добавим функции, обрабатывающие события передвижения trackBar-ов

private void trackBar1_Scroll(object sender, EventArgs e)

{

if (Thread_1.IsAlive) Thread_1.Priority = (ThreadPriority)

Enum.Parse(typeof(ThreadPriority), Convert.ToString

(trackBar1.Value - 1));

}

private void trackBar2_Scroll(object sender, EventArgs e)

{

if (Thread_2.IsAlive) Thread_2.Priority = (ThreadPriority)Enum.Parse(typeof(ThreadPriority), Convert.ToString(trackBar2.Value - 1));

}

private void trackBar3_Scroll(object sender, EventArgs e)

{

if (Thread_3.IsAlive) Thread_3.Priority = (ThreadPriority)

Enum.Parse(typeof(ThreadPriority), Convert.ToString

(trackBar3.Value - 1));

}

private void trackBar4_Scroll(object sender, EventArgs e)

{

pause = trackBar4.Value * 100;

}

Результат работы программы представлен на рис. 8.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]