
- •Лекция 1. Введение.
- •Лекция 2-3. Основные понятия. Типы данных.
- •Основные типы данных
- •Лекция 4 Выражения. Классификация операторов
- •Операторы объявлений типов и переменных
- •Операторы вызова функций
- •Математические и логические операции. Условная операция. Математические операции для целочисленных и вещественных вычислений.
- •Математические операции только для целочисленных вычислений
- •Логические операции.
- •Условная операция.
- •Операторы управления.
- •Оператор ветвления.
- •Оператор выбора.
- •Лекция 5. Циклы
- •Цикл while
- •Цикл for
- •Операция "запятая"
- •Цикл с условием на выходе: do while
- •Какой цикл лучше?
- •Другие управляющие операторы: break, continue, goto.
- •Лекция 6. Структуры данных. Массивы. Объединения. Строковые литералы.
- •1. Объявление массива
- •2. Инициализация массивов
- •3. 1 Работа с массивами
- •3.2. Обработка массивов
- •3.3. Ввод/вывод массивов
- •Объединения в c
- •Лекция 7. Функции. Рекурсия. 1 часть.
- •Лекция 8. Функции. Рекурсия. 2 часть.
- •Лекция 9. Указатели.
- •Функции управление памятью
- •Лекция 10. Динамические структуры данных.
- •Лекция 11. Файлы
- •Лекция 13. Объектно-ориентированные модели. Составные части объектного подхода.
- •Лекция 14. Классы. Конструкторы и деструкторы.
- •Лекция 15. Простое наследование классов
- •Лекция 16. Перегрузка функций
- •Лекция 17. Перегрузка операторов
- •Лекция 18. Друзья
- •Лекция 19. Шаблоны. Стандартная библиотека шаблонов
- •Лекция 20. Исключительные ситуации
- •Лекция 3.2. Проектирование структуры приложения. Система меню
- •Лекция 3.3.1. Стандартные и дополнительные компоненты
- •Лекция 3.3.2. Компоненты страницы Win32. Системные компоненты.
- •Лекция 3.4. Проектирование структуры данных
- •Лекция 3.6. Компоненты ActiveX. Графические компоненты
- •3.6.1.Компоненты ActiveX.
- •3.6.2. Графические компоненты
- •Лекция 4.1. Основные понятия языка. Переменные, операции, выражения. Операторы
- •Класс Array
- •Массивы как коллекции
- •Сортировка и поиск. Статические методы класса Array
- •Лекция 4.3. Делегаты, события и потоки выполнения. Работа с файлами библиотеки, атрибуты, директивы
- •Описание делегатов
- •Использование делегатов
- •Паттерн "наблюдатель"
- •Операции
- •Передача делегатов в методы
- •События
- •Многопоточные приложения
- •Класс Thread
- •Асинхронные делегаты
- •Лекция 5.1. Методы конструирования сложных программных систем
- •Inline-ассемблер в Delphi
- •Лекция 5.2. Разработка динамических библиотек
- •Для начала - что это такое ?
- •Далее разберемся: какая может быть польза от dll
Многопоточные приложения
Приложение .NET состоит из одного или нескольких процессов. Процессу принадлежат выделенная для него область оперативной памяти и ресурсы. Каждый процесс может состоять из нескольких доменов (частей) приложения, ресурсы которых изолированы друг от друга. В рамках домена может быть запущено несколько потоков выполнения. Поток (thread) представляет собой часть исполняемого кода программы. Иногда термин "thread" переводится буквально — "нить", чтобы отличить его от потоков ввода-вывода. Поэтому в литературе можно встретить и термин "многонитевые приложения".
В каждом процессе есть первичный поток, исполняющий роль точки входа в приложение. Для консольных приложений это метод Main.
Многопоточные приложения создают как для многопроцессорных, так и для однопроцессорных систем. Основной целью при этом являются повышение общей производительности и сокращение времени реакции приложения. Управление потоками осуществляет операционная система. Каждый поток получает некоторое количество квантов времени, по истечении которого управление передается другому потоку. Это создает у пользователя однопроцессорной машины впечатление одновременной работы нескольких потоков и позволяет, к примеру, выполнять ввод текста одновременно с длительной операцией по передаче данных.
Недостатки многопоточности:
большое количество потоков ведет к увеличению накладных расходов, связанных с переключением потоков, что снижает общую производительность;
возникают проблемы синхронизации данных, связанные с потенциальной возможностью доступа к одним и тем же данным со стороны нескольких потоков (например, если один поток начинает изменение общих данных, а отведенное ему время истекает, доступ к этим же данным может получить другой поток, который, изменяя данные, необратимо их повреждает).
Класс Thread
В .NET многопоточность поддерживается в основном с помощью пространства имен System.Threading. Некоторые типы этого пространства описаны в таблице 4.3.1.
Таблица 4.3.1. Некоторые типы пространства имен System.Threading |
|
Тип |
Описание |
Monitor |
Класс, обеспечивающий синхронизацию доступа к объектам |
Mutex |
Класс-примитив синхронизации, который используется также для синхронизации между процессами |
Thread |
Класс, который создает поток, устанавливает его приоритет, получает информацию о состоянии |
ThreadPool |
Класс, используемый для управления набором взаимосвязанных потоков — пулом потоков |
Timer |
Класс, определяющий механизм вызова заданного метода в заданные интервалы времени для пула потоков |
WaitHandle |
Класс, инкапсулирующий объекты синхронизации, которые ожидают доступа к разделяемым ресурсам |
ThreadStart |
Делегат, представляющий метод, который должен быть выполнен при запуске потока |
TimerCallback |
Делегат, представляющий метод, обрабатывающий вызовы от класса Timer |
WaitCallback |
Делегат, представляющий метод для элементов класса ThreadPool |
ThreadPriority |
Перечисление, описывающее приоритет потока |
ThreadState |
Перечисление, описывающее состояние потока |
Первичный поток создается автоматически. Для запуска вторичных потоков используется класс Thread. При создании объекта-потока ему передается делегат, определяющий метод, выполнение которого выделяется в отдельный поток:
Thread t = new Thread ( new ThreadStart( имя_метода ) );
После создания потока заданный метод начинает в нем свою работу, а первичный поток продолжает выполняться. В листинге приведен пример одновременной работы двух потоков.
using System;
using System.Threading;
namespace ConsoleApplication1
{ class Program
{
static public void Hedgehog() // метод для вторичного потока
{
for ( int i = 0; i < 6; ++i )
{
Console.Write(" " + i ); Thread.Sleep( 1000 );
}
}
static void Main()
{
Console.WriteLine( "Первичный поток " +
Thread.CurrentThread.GetHashCode() );
Thread ta = new Thread( new ThreadStart(Hedgehog) );
Console.WriteLine( "Вторичный поток " + ta.GetHashCode() );
ta.Start();
for ( int i = 0; i > -6; --i )
{
Console.Write( " " + i ); Thread.Sleep( 400 );
}
}
}
}
Листинг 10.6. Создание вторичного потока
Результат работы программы:
Первичный поток 1
Вторичный поток 2
0 0 -1 -2 1 -3 -4 2 -5 3 4 5
В листинге используется метод Sleep, останавливающий функционирование потока на заданное количество миллисекунд. Как видите, оба потока работают одновременно. Если бы они работали с одним и тем же файлом, он был бы испорчен так же, как и приведенный вывод на консоль, поэтому такой способ распараллеливания вычислений имеет смысл только для работы с различными ресурсами.
В таблице 4.3.2 перечислены основные элементы класса Thread.
Таблица 4.3.2. Основные элементы класса Thread |
||
Элемент |
Вид |
Описание |
CurrentThread |
Статическое свойство |
Возвращает ссылку на выполняющийся поток (только для чтения) |
Name |
Свойство |
Установка текстового имени потока |
Priority |
Свойство |
Получить/установить приоритет потока (используются значения перечисления ThreadPriority ) |
ThreadState |
Свойство |
Возвращает состояние потока (используются значения перечисления ThreadState ) |
Abort |
Метод |
Генерирует исключение ThreadAbortException. Вызов этого метода обычно завершает работу потока |
Sleep |
Статический метод |
Приостанавливает выполнение текущего потока на заданное количество миллисекунд |
Interrupt |
Метод |
Прерывает работу текущего потока |
Join |
Метод |
Блокирует вызывающий поток до завершения другого потока или указанного промежутка времени и завершает поток |
Resume |
Метод |
Возобновляет работу после приостановки потока |
Start |
Метод |
Начинает выполнение потока, определенного делегатом ThreadStart |
Suspend |
Метод |
Приостанавливает выполнение потока. Если выполнение потока уже приостановлено, то игнорируется |
Можно создать несколько потоков, которые будут совместно использовать один и тот же код. Пример приведен в листинге.
using System;
using System.Threading;
namespace ConsoleApplication1
{
class Class1
{ public void Do()
{
for ( int i = 0; i < 4; ++i )
{ Console.Write( " " + i ); Thread.Sleep( 3 ); }
}
}
class Program
{ static void Main()
{
Class1 a = new Class1();
Thread t1 = new Thread( new ThreadStart( a.Do ) );
t1.Name = "Second";
Console.WriteLine( "Поток " + t1.Name );
t1.Start();
Thread t2 = new Thread( new ThreadStart( a.Do ) );
t2.Name = "Third";
Console.WriteLine( "Поток " + t2.Name );
t2.Start();
}
}
}
Результат работы программы:
Поток Second
Поток Third
0 0 1 1 2 2 3 3
Варианты вывода могут несколько различаться, поскольку один поток прерывает выполнение другого в неизвестные моменты времени.
Для того чтобы блок кода мог использоваться в каждый момент только одним потоком, применяется оператор lock. Формат оператора:
lock ( выражение ) блок_операторов
Выражение определяет объект, который требуется заблокировать. Для обычных методов в качестве выражения используется ключевое слово this, для статических — typeof( класс ).Блок операторов задает критическую секцию кода, которую требуется заблокировать.
Например, блокировка операторов в приведенном ранее методе Do выглядит следующим образом:
public void Do()
{
lock( this )
{
for ( int i = 0; i < 4; ++i )
{ Console.Write( " " + i ); Thread.Sleep( 30 ); }
}
}
Для такого варианта метода результат работы программы изменится:
Поток Second
Поток Third
0 1 2 3 0 1 2 3