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

Лабораторна робота №2

Теоретичні відомості:

Створення і запуск потоків

Для створення потоків використовується конструктор класу Thread, що приймає як параметр делегат типу ThreadStart, що вказує метод, якому потрібно виконати. Делегат ThreadStart визначається так:

public delegate void ThreadStart();

Виклик методу Start починає виконання потоку. Потік продовжується до виходу з методу, що виконується. От приклад, що використовує повний синтаксис C# для створення делегата ThreadStart:

class ThreadTest

{

static void Main()

{

Thread t = new Thread(new ThreadStart(Go));

t.Start(); // Виконати Go() у новому потоці.

Go(); // Одночасно запустити Go() у головному потоці.

}

static void Go() { Console.WriteLine("hello!"); }

У цьому прикладі потік виконує метод Go() одночасно з головним потоком. Результат – два майже одночасних «hello»:

hello!

hello!

Потік можна створити, використовуючи для присвоювання значень делегатам більш зручний скорочений синтаксис C#:

static void Main()

{

Thread t = new Thread(Go); // Без явного використання ThreadStart

t.Start();

...

}

static void Go() { ... }

У цьому випадку делегат ThreadStart виводиться компілятором автоматично. Інший варіант скороченого синтаксису використовує анонімний метод для створення потоку:

static void Main()

{

Thread t = new Thread(delegate() { Console.WriteLine("Hello!"); });

t.Start();

}

Потік має властивість IsAlive, що повертає true після виклику Start() і до завершення потоку.

Потік, що закінчив виконання, не може бути початий заново.

Передача даних у ThreadStart

Допустимо, що в розглянутому вище прикладі ми захочемо більш явно розрізняти виведення кожного з потоків, наприклад, по регістрі символів. Можна домогтися цього, передаючи відповідний прапор у метод Go(), але в цьому випадку не можна використовувати делегат ThreadStart, так він не приймає аргументів. На щастя, .NET Framework визначає іншу версію делегата – ParameterizedThreadStart, що може приймати один аргумент:

public delegate void ParameterizedThreadStart(object obj);

Попередній приклад можна переписати так:

class ThreadTest

{

static void Main()

{

Thread t = new Thread(Go);

t.Start(true); // == Go(true)

Go(false);

}

static void Go(object upperCase)

{

bool upper = (bool)upperCase;

Console.WriteLine(upper ? "HELLO!" : "hello!");

}

}

Консольний виведення:

hello!

HELLO!

У цьому прикладі компілятор автоматично виводить делегат ParameterizedThreadStart, тому що метод Go() приймає як параметр один object. З тим же успіхом можна було написати:

Thread t = new Thread(new ParameterizedThreadStart(Go));

t.Start(true);

Особливість використання ParameterizedThreadStart полягає в тому, що перед використанням потрібно привести аргумент із типу object до потрібного типу (у даному випадку bool). До того ж існує только версія, що приймає єдиний аргумент.

Як альтернативу можна використовувати анонімний метод:

static void Main()

{

Thread t = new Thread(delegate(){ WriteText("Hello"); });

t.Start();

}

static void WriteText(string text) { Console.WriteLine(text); }

Зручність полягає в тому, що потрібний метод (у даному випадку WriteText) можна викликати з будь-якою кількістю аргументів і без усякого приведення типів. Однак потрібно взяти до уваги особливість семантики анонімних методів, зв'язану з зовнішньої перемінної, котра стає очевидної в наступному прикладі:

static void Main()

{

string text = "Before";

Thread t = new Thread(delegate() { WriteText(text); });

text = "After";

t.Start();

}

static void WriteText(string text) { Console.WriteLine(text); }

Консольний виведення:

After

ПОПЕРЕДЖЕННЯ

Анонімні методи відкривають вигадливі можливості ненавмисної взаємодії через зовнішні перемінні, якщо вони змінюються ким-небудь після старту потоку. Планової взаємодії (звичайно через поля класу) як правило більш ніж достатнє! Найкраще, як тільки почалося виконання потоку, розглядати зовнішні перемінні як перемінні тільки для читання – за винятком хіба що реалізацій з відповідними блокуваннями на обох сторонах.

Інший спосіб передачі даних у потік складається в запуску в потоці методу визначеного екземпляра об'єкта, а не статичного методу. Тоді властивості обраного екземпляра об'єкта будуть визначати поводження потоку, як у наступному варіанті оригінального приклада:

class ThreadTest

{

bool upper;

static void Main()

{

ThreadTest instance1 = new ThreadTest();

instance1.upper = true;

Thread t = new Thread(instance1.Go);

t.Start();

ThreadTest instance2 = new ThreadTest();

instance2.Go(); // Запуск у головному потоці - з upper=false

}

void Go(){ Console.WriteLine(upper ? "HELLO!" : "hello!"); }

Соседние файлы в папке Semestr2