Класс System.Threading.Thread — КиберПедия 

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

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

Класс System.Threading.Thread

2017-12-21 311
Класс System.Threading.Thread 0.00 из 5.00 0 оценок
Заказать работу

Операционная система позволяет программисту осуществлять управление потоками (при наличии соответствующих прав у пользователя от лица которого выполняется программа). Как правило, это происходит посредством вывоза соответствующих функций ядра операционной системы, что осуществляется через интерфейс прикладного программирования (Application Programming Interface - API) операционной системы. В случае с Microsoft Windows – WinAPI. Классы платформы.NET скрывают низкоуровневые API ОС, позволяя использовать более простую объектно-ориентированную модель. Работы с потоками в.NET осуществляется при помощи классов пространства имён System.Threading, в основном – при помощи класса System.Threading.Thread.

Создание потоков и объектов Thread

Как отмечалось, работа с потоками происходит посредством класса System.Threading.Thread. Вы можете создавать экземпляры Thread двумя способами. Один вы уже видели: это создание нового потока и получение объекта Thread, позволяющего манипулировать новым потоком. Другой способ получить объект Thread для потока, который выполняется в данный момент, — вызов статического метода Thread.CurrentThread (результат – объект Thread для того потока, который вызвал этот метод).

Управление потоками

Рассмотрим некоторые приёмы работы с классом Thread:

using System;

using System.Threading;

 

namespace multithreading_02

{

class ThreadSleepApp

{

/// <summary>

/// Код потока, который будет выполняться параллельно главному потоку.

/// </summary>

public static void ThreadProc()

{

Console.WriteLine("Второй поток работает.");

int sleepTime = 5000;

Console.WriteLine("Второй поток засыпает на {0} секунд.",

sleepTime/1000);

Thread.Sleep(sleepTime);

Console.WriteLine("Второй поток снова работает.");

}

 

/// <summary>

/// Главный поток приложения.

/// </summary>

public static void Main()

{

Console.WriteLine("Главный поток - создаём ещё один поток.");

Thread t = new Thread(ThreadProc);

t.Start();

Console.WriteLine("Главный поток - второй поток запущен.");

}

}

}

 

Как видите, мы ещё больше упростили создание объекта Thread в Main(). В остальном функция Main() эквивалента предыдущему примеру. Остаётся выяснить что же делает функция ThreadProc(). Итак, результаты работы это программы:

Главный поток - создаём ещё один поток.

Главный поток - второй поток запущен.

Второй поток работает.

Второй поток засыпает на 5 секунд.

Второй поток снова работает.

Press any key to continue...

Вам уже известно, что порядок строк, выводимых программой, может немного отличаться от приведенного выше. Тем не менее, строка «Второй поток снова работает.» не появится раньше, чем через 5 секунд после старта программы. Дело в том, что второй поток «засыпает» на 5 секунд, т.е. сообщает ОС, что в ближайшие 5000 миллисекунд ему не понадобятся кванты времени для работы. Как мы видим, делает от это сам (т.е. «добровольно», по собственному желанию) вызывая метод Sleep().

В принципе, есть два варианта вызова метода Thread.Sleep(). Первый — вызов Thread. Sleep() со значением 0. При этом вы заставите текущий поток освободить неиспользованный остаток своего кванта. С другой стороны, передача в качестве параметра положительного числа приведёт к остановке выполнения потока на заданное количество миллисекунд. При передаче же значения Timeout.Infinite поток будет приостановлен на неопределенно долгий срок, пока это состояние потока не будет прервано другим потоком, вызвавшим метод приостановленного потока Thread.Interrupt() – прервать работу потока, находящегося в состоянии WaitSleepJoin. Перевод потока в состояние бесконечного ожидания и вывод его из этого состояния при помощи метода Thread.Interrupt() не является хорошей (и как следствие широко распространённой практикой), но это приводит нас к пониманию того, что поток обладает «состоянием»: он может «выполняться», «быть остановленным», «спать» и так далее. Полный граф состояний потока приведён ниже. Обратите внимание, что переход между состояниями можно инициировать соответствующими методами. Узнать текущее состоянии потока можно с помощью его свойства ThreadState.

 

Вернёмся к управлению потоками. Второй способ приостановить исполнение потока — вызов метода Thread.Suspend(). Между этими методиками (Interrupt() и Suspend()) есть несколько важных отличий. Во-первых, можно вызвать метод Thread. Suspend() для потока, выполняющегося в текущий момент, или для любого другого потока. Во-вторых, если таким образом приостановить выполнение потока, любой другой поток способен возобновить его выполнение с помощью метода Thread.Resume(). Обратите внимание, что, когда один поток приостанавливает выполнение другого, первый поток не блокируется. Возврат управления после вызова происходит немедленно. Кроме того, единственный вызов Thread.Resume() возобновит исполнение данного потока независимо от числа вызовов метода Thread.Suspend(), выполненных ранее.

Таким образом, пара методов Suspend()/Resume() является предпочтительной, когда речь идёт о временной приостановке работы потока. Здесь мы имеем в виду, что этот механизм является удобным, когда речь идёт об управлении одним потоком выполнением другого потока. Когда же поток хочет приостановить своё собственное выполнение, то более применим метод Sleep(), как сделано в предыдущем примере.

Уничтожение потоков

Уничтожить поток можно вызовом метода Thread.Abort(). Исполняющая среда насильно завершает выполнение потока, генерируя исключение ThreadAbortException (это исключение возникнет в коде потока, для которого был вызван метод Abort()). Даже если поток попытается обработать ThreadAbortException, исполняющая среда этого не допустит. Однако она исполнит код из блока finally потока, выполнение которого прервано, если этот блок присутствует (что позволяет среагировать на аварийное завершение). Проиллюстрируем это следующим примером. После запуска главный поток создаёт и запускает второй (рабочий) поток. Далее, выполнение метода Main приостанавливается на 5 секунд, чтобы дать исполняющей среде время для запуска рабочего потока. После запуска рабочий поток считает до десяти, останавливаясь после каждого отсчета на секунду. Когда выполнение метода Main возобновляется после пятисекундной паузы, он прерывает выполнение рабочего потока (после этого исполняется блок finally).

using System;

using System.Threading;

 

class ThreadAbortApp {

 

public static void ThreadProc()

{

try

{

Console.WriteLine("Рабочий поток запущен.");

for (int i = 0; i < 10; i++) {

Thread. Sleep(1000);

Console.WriteLine("Рабочий поток -> {0}", i);

}

Console.WriteLine("Рабочий поток завершён");

}

catch(ThreadAbortException e)

{

// ThreadAbortException здесь обработано не будет!

}

finally

{

Console.WriteLine("В рабочем потоке возникло необработанное исключение!");

}

}

 

public static void Main()

{

Console.WriteLine("Главный поток - запускаем рабочий поток.");

Thread t = new Thread(ThreadProc);

t.Start();

 

Console.WriteLine("Главный поток - засыпаем на 5 секунд.");

Thread.Sleep(5000);

 

Console.WriteLine("Главный поток - прерываем рабочий поток.");

t.Abort();

}

}

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

Главный поток - запускаем рабочий поток.

Рабочий поток запущен.

Главный поток - засыпаем на 5 секунд.

Рабочий поток -> 0

Рабочий поток -> 1

Рабочий поток -> 2

Рабочий поток -> 3

Рабочий поток -> 4

Главный поток - прерываем рабочий поток.

В рабочем потоке возникло необработанное исключение!

Press any key to continue...

Планирование потоков

При переключении процессора по окончании выделенного потоку кванта времени, процесс выбора следующего потока, предназначенного для исполнения, далеко не произволен. У каждого потока есть приоритет, указывающий процессору, как должно планироваться выполнение этого потока по отношению к другим потокам системы. Для потоков, создаваемых в период выполнения, уровень приоритета по умолчанию равен Normal. Для просмотра и установки этого значения служит свойство Thread.Priority. Свойству Thread.Priority можно присвоить значение типа Thread.ThreadPriority, которое представляет собой перечисление, определяющее значения Highest, AboveNormal, Normal, BelowNormal и Lowest (наивысший, выше нормального, нормальный, ниже нормального, низший).

Рассмотрим пример:

using System;

using System.Threading;

 

class ThreadAbortApp

{

/// <summary>

/// Код рабочего потока.

/// </summary>

public static void ThreadProc(object args)

{

string name = args.ToString();

Console.WriteLine("Рабочий поток запущен ({0}).", name);

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

{

// Цикл с бесполезной работой (для загрузки процессора).

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

Math.Sin(Math.Cos(j) + Math.Atan(j) * Math.Sin(Math.Cos(j)

+ Math.Atan(i)));

Console.WriteLine("{0} -> {1}", name, i);

}

Console.WriteLine("Рабочий поток завершён ({0}).", name);

}

 

public static void Main()

{

// Разрешить данному процессу использовать только один процессор.

System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity

= new IntPtr(0x0001);

 

// Создаём два рабочих потока.

Console.WriteLine("Главный поток - запускаем рабочие потоки.");

Thread t1 = new Thread(ThreadProc);

Thread t2 = new Thread(ThreadProc);

 

// Устанавливаем приоритеты потоков.

t1.Priority = ThreadPriority.BelowNormal;

t2.Priority = ThreadPriority.AboveNormal;

 

// Запускаем потоки.

t1.Start("первый поток");

t2.Start("второй поток");

 

// Ждём завершения обоих потоков.

t1.Join();

t2.Join();

Console.WriteLine("Главный поток - выполнение обоих потоков завершено.");

}

}

 

Результаты работы программы (потоки запущены с одинаковым приоритетом):

Главный поток - запускаем рабочие потоки.

Рабочий поток запущен (первый поток).

Рабочий поток запущен (второй поток).

первый поток -> 0

второй поток -> 0

первый поток -> 1

второй поток -> 1

первый поток -> 2

второй поток -> 2

первый поток -> 3

второй поток -> 3

второй поток -> 4

первый поток -> 4

второй поток -> 5

первый поток -> 5

второй поток -> 6

первый поток -> 6

второй поток -> 7

первый поток -> 7

второй поток -> 8

первый поток -> 8

второй поток -> 9

Рабочий поток завершён (второй поток).

первый поток -> 9

Рабочий поток завершён (первый поток).

Главный поток - выполнение обоих потоков завершено.

Press any key to continue...

 

Потоки запущены с разными приоритетами:

Главный поток - запускаем рабочие потоки.

Рабочий поток запущен (первый поток).

Рабочий поток запущен (второй поток).

второй поток -> 0

второй поток -> 1

второй поток -> 2

второй поток -> 3

второй поток -> 4

второй поток -> 5

второй поток -> 6

второй поток -> 7

второй поток -> 8

второй поток -> 9

Рабочий поток завершён (второй поток).

первый поток -> 0

первый поток -> 1

первый поток -> 2

первый поток -> 3

первый поток -> 4

первый поток -> 5

первый поток -> 6

первый поток -> 7

первый поток -> 8

первый поток -> 9

Рабочий поток завершён (первый поток).

Главный поток - выполнение обоих потоков завершено.

Press any key to continue...

 

Обратите внимание, что второй поток завершился раньше, несмотря на то, что был запушен позже первого.

В приведённом примере также демонстрируются ещё два новых механизма работы с потоками:

  • передача параметров потоку;
  • ожидание завершения выполнения потоков.

Каждому потоку передаётся его «имя» - это позволяет различать вывод первого и второго потоков на консоли. В реальных программах для этой цели используется свойство Thread.Name, что избавляет от необходимости передавать лишние параметры потоку.

Ожидание завершения выполнения потоков также является одним из важнейших механизмов работы с потоками. В.NET это осуществляется при помощи метода Thread.Join(), который блокирует вызывающий поток до завершения потока, метод Join() которого был вызван. Т.е. в нашем примере:

public static void Main()

{

...

t1.Join();

t2.Join();

...

}

выполнение метода Main() будет приостановлено сначала до тех пор, пока первый поток не завершится, а затем – до завершения и второго потока. Таким образом, мы дождёмся завершения обоих потоков.

 

См. также:

· System.Threading - пространство имен

· Thread - класс

· Создание потоков (Руководство по программированию на C#)

· Использование потоков (Руководство по программированию на C#)

· Синхронизация потоков (Руководство по программированию на C#)

Задание категории А.

Написать приложение, содержащее не менее двух тредов. Каждый из этих тредов должен искать файлы:

- с определенным заданным шаблоном;

- содержащие в своем составе определенную строку;

- начиная с определенного директория;

- обеспечить возможность поиска в поддиректориях.


Поделиться с друзьями:

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

Историки об Елизавете Петровне: Елизавета попала между двумя встречными культурными течениями, воспитывалась среди новых европейских веяний и преданий...

Семя – орган полового размножения и расселения растений: наружи у семян имеется плотный покров – кожура...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.068 с.