Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 158
Текст из файла (страница 158)
Потомокв1 начат со счета 5 Потомок Ф2 начат со счета 3 В потоке Потомок $2, Соопс = 0 В потоке Потомок В1, Соопс = 0 В потоке Потомок Ф1, Соопс = 1 В потоке Потомок Ф2, Соипс = 1 В потоке Потомок Е2, Сооьс = 2 Потомок В2 завершен. В потоке Потомок Ф1, Соопс = 2 В потоке Потомок Е1, Соипс = 3 В потоке Потомок Ф1, Соопс = 4 Потомок В1 завершен. Основной поток завершен. Как следует из приведенного выше результата, первый поток повторяется пять раз, а второй — три раза. Число повторений указывается в конструкторе класса мутпгеаб и затем передается методу Впп (), служащему в качестве точки входа в поток, с помощью парамегризированной формы РагашегегйзебТЬгеабЯСагг метода Вгагс () . Свойство х ЗВас~сдх оппй Как упоминалось выше, в среде .НЕТ Егашеччог)г определены две разновидности потоков: приоритетный и фоновый. Единственное отличие между ними заключается в том, что процесс не завершится до тех пор, пока не окончится приоритетный поток, тогда как фоновые потоки завершаются автоматически по окончании всех приоритетных потоков.
По умолчанию создаваемый поток становится приоритетным. Но его можно сделать фоновым, используя свойство 1зВаскогоппс(, определенное в классе тьгеаб, следующим образом: роЬ11с Ьоо1 1звасхдгоопб ( чем зес; ) 800 часть )). Библиотека Сз Для того чтобы сделать поток фоновым, достаточно присвоить логическое значение сгпе свойству 1знас)когоппс). А логическое значение Та1зе указывает на то, что поток является приоритетным. Приоритеты потоков У каждого потока имеется свой приоритет, который отчасти определяет, насколько часто поток получает доступ к ЦП.
Вообще говоря, низкоприоритетные потоки получают доступ к ЦП реже, чем высокоприоритетные. Таким образом, в течение заданного промежутка времени низкоприоритетному потоку будет доступно меньше времени ЦП, чем высокоприоритетному. Как и следовало ожидать, время ЦП, получаемое потоком, оказывает определяющее влияние на характер его выполнения и взаимодействия с другими потоками, исполняемыми в настоящий момент в системе. Следует иметь в виду, что, помимо приоритета, на частоту доступа потока к ЦП оказывают влияние и другие факторы. Так, если высокоприоритетный поток ожидает доступа к некоторому ресурсу, например для ввода с клавиатуры, он блокируется, а вместо него выполняется низкоприоритетный поток.
В подобной ситуации низкоприоритетный поток может получать доступ к ЦП чаще, чем высокоприоритетный поток в течение определенного периода времени. И наконец, конкретное планирование задач на уровне операционной системы также оказывает влияние на время ЦП, выделяемое для потока. Когда порожденный поток начинается, он получает приоритет, устанавливаемый по умолчанию. Приоритет потока можно изменить с помошью свойства Рг1ог1су, являющегося членом класса т)тгеас).
Ниже приведена общая форма данного свойства. Рио11с Т)тгеат)Рг1ог1гу Рг1оггку) Рек; зест ) где тпгеас)Рг1ог1гу обозначает перечисление, в котором определяются приведенные ниже значения приоритетов. ТьгеаПРггоггсу.и1чнезк тьгеапРггогггу.аьоченогяа1 ТигеаПРггоггсу.ногяа1 ТьгеаПРггогъсу.ве1оииогпа1 Тпгеак)Рггоггсу.ьоьиес По умолчанию для потока устанавливается значение приоритета Т)тгеас)Рг1ог1 у. Ногва1. Для того чтобы стало понятнее влияние приоритетов на исполнение потоков, обратимся к примеру, в котором выполняются два потока: один с более высоким приоритетом, чем другой. Оба потока создаются в качестве экземпляров объектов класса мут)тгеас). В методе цпп () организуется цикл, в котором подсчитывается определенное число повторений.
Цикл завершается, когда подсчет достигает величины 1000000000 или когда статическая переменная егор получает логическое значение ггие. Первоначально переменная асор получает логическое значение Та1зе. В первом потоке, где производится подсчет до 1000000000, устанавливается логическое значение ггпе переменной егор. В силу этого второй поток оканчивается на следующем своем интервале времени. На каждом шаге цикла строка в переменной спггепгнаате проверяется на наличие имени исполняемого потока. Если имена потоков не совпадают, это означает, что произошло переключение выполняемых задач.
Всякий раз, когда происходит переключение задач, имя нового потока отображается и присваивается переменной спггепгнаате. Это дает возможность отследить частоту доступа потока к ЦП. По окончании обоих потоков отображается число повторений цикла в каждом из них. Глава 23. Многопоточное программирование 801 // Продемонстрировать влияние приоритетов потоков.
из1пд Яузгепи изгпд Яузсев.ТЬгеабгпдт с1азя Мутпгеаб ( риЫ1с хпс Соипст риЫгс Тпгеаб тьгбт зсасас Ьоо1 асор = Та1зет згаггс зггтпд сшгепгмавет /* Сконструировать новый поток. Обратите внимание на то, что данный конструктор еще не начинает выполнение потоков. */ риЫгс Мутпгеаб(зсгтпд паве) ( Соипс = 0; тпгб — пен ТЬгеаи(ГЬ1з.аип)Г ТЬгб.Маме = павел сиггепснаве = паве; ) // Начать выполнение нового потока.
чо1б Нип() ( сопзо1е.мгтгеьгпе("поток " + тьгб.маве + " начат."); с(о ( Соипс++; 1Т(сиггепГМаве != Тпгб.наве) ( сиггепсыаве = ТЬгб.навет Сопзо1е.мг1сетапе("В потоке " + сшгепсыаве)т ) ) нЬ11е(асор == Та1яе аа Соипс < 1000000000)т ягор = сгиег Сопзо1е.Иг1сев1пе("Поток " + Тпгг(.маме + " эавервен.")т с1азз Ргтог1сурево ( зсасгс чо1б Ма1п() ( МуТЬгеаб вг1 = пен Мутпгеаг(("с высоким приоритетом")> МуТЬгеаб вс2 = пен Мутпгеаб("с низким приоритетом"); // установить приоритеты для потоков. вс1.ТЬгб.Ргаоггсу = ТЬгеабРг1огтсу.АЬочемогва1) вс2.ТЬгб.Рг1огаау = ТЬгеабРг1ог1су.Ве1онногва1г // Начать потоки.
вг).тьгб.ягагг()т вс2.ТЬгб.нсагс()т вгт.тьгб.оо п()т 802 Часть )!. Библиотека С№ вс2.тьгк(.до1п П т Сопяо1е.Игггеьгпе()т Сопяо1е.иггкеьапе("Поток " + вс1.тантк).паве + " досчитал до " + вс1.Сопля)т Сопяо1е.нг1се01пе("Поток " + вГ2.тантк).паве + " досчитал до " + вс2.Сопля)) Вот к какому результату приводит выполнение этой программы: Поток с высокиы приоритетом начат. В потоке с высоким приоритетом Поток с низким приоритетом начат.
В потоке с иизким приоритетом В потоке с высоким приоритетом В потоке с иизким приоритетом В потоке с высоким приоритетом В потоке с низким приоритетом В потоке с высоким приоритетом В потоке с низким приоритетом В потоке с высоким приоритетом В потоке с низким приоритетом В потоке с высоким приоритетом Поток с высоким приоритетом завершен. Поток с низким приоритетом завершен. Поток с высоким приоритетом досчитал до 1000000000 Поток с низким приоритетом досчитал до 23996334 Судя по результату, высокоприоритетный поток получил около 989ь всего времени, которое было выделено для выполнения этой программы.
Разумеется, конкретный результат может отличаться в зависимости от быстродействия ЦП и числа других задач, решаемых в системе, а также от используемой версии ЪМП()отея. Многопоточный код может вести себя по-разному в различных средах, поэтому никогда не следует полагаться на результаты его выполнения только в одной среде. Так, было бы ошибкой полагать, что низкоприоритетный поток из приведенного выше примера будет всегда выполняться лишь в течение небольшого периода времени до тех пор, пока не завершится высокоприоритетный поток. В другой среде высокоприоритетный поток может, например, завершиться еще до того, как низкоприоритетный поток выполнится хотя бы один раз.
Синхронизация Когда используется несколько потоков, иногда приходится координировать действия двух или более потоков. Процесс достижения такой координации называется пгихроиизат(ией. Наиболее распространенной причиной для применения синхронизации служит необходимость разделять среди двух или более потоков общий ресурс, который может быть одновременно доступен только одному потоку. Например, когда в одном потоке выполняется запись информации в файл, второму потоку должно быть запрещено делать это в тот же самый момент времени. Синхронизация требуется и в том случае, если один Глава 23. Многопоточное программирование 803 поток ожидает событие, вызываемое другим потоком. В подобной ситуации требуются какие-то средства, позволяющие приостановить один из потоков до тех пор, пока не произойдет событие в другом потоке. После этого ожидающий поток может возобновить свое выполнение.
В основу синхронизации положено понятие блокировки, посредством которой организуется управление доступом к кодовому блоку в объекте. Когда объект заблокирован одним потоком, остальные потоки не могут получить доступ к заблокированному кодовому блоку.
Когда же блокировка снимается одним потоком, объект становится доступным для использования в другом потоке. Средство блокировки встроено в язык С(). Благодаря этому все объекты могут быть синхронизированы. Синхронизация организуется с помощью ключевого слова 1ос)г. Она была предусмотрена в С(г с самого начала, и поэтому пользоваться ею намного проще, чем кажется на первый взгляд. В действительности синхронизация объектов во многих программах на С(Г происходит практически незаметно.
Ниже приведена общая форма блокировки. 1осн(1осаппу) ( // синхронизируемые операторы где 1ос)гоЬт' обозначает ссылку на синхронизируемый объект. Если же требуется синхронизировать только один оператор, то фигурные скобки не нужны. Оператор 1оск гарантирует, что фрагмент кода, защищенный блокировкой для данного объекта, будет использоваться только в потоке, получающем эту блокировку. А все остальные потоки блокируются до тех пор, пока блокировка не будет снята. Блокировка снимается по завершении защищаемого ею фрагмента кода. Блокируемым считается такой объект, который представляет синхронизируемый ресурс. В некоторых случаях им оказывается экземпляр самого ресурса или же произвольный экземпляр объекта, используемого для синхронизации.
Следует, однако, иметь в виду, что блокируемый объект не должен быть общедоступным, так как в противном случае он может быть заблокирован из другого, неконтролируемого в программе фрагмента кода и в дальнейшем вообще не разблокируется. В прошлом для блокировки объектов очень часто применялась конструкция 1ос)г (СП15) .