Шрифт:
Интервал:
Закладка:
// Продемонстрировать влияние приоритетов потоков.
using System;
using System.Threading;
class MyThread {
public int Count;
public Thread Thrd;
static bool stop = false;
static string currentName;
/* Сконструировать новый поток. Обратите внимание на то, что данный конструктор еще не начинает выполнение потоков. */
public MyThread(string name) {
Count = 0;
Thrd = new Thread(this.Run);
Thrd.Name = name; currentName = name;
}
// Начать выполнение нового потока,
void Run() {
Console.WriteLine("Поток " + Thrd.Name + " начат.");
do {
Count++;
if(currentName != Thrd.Name) {
currentName = Thrd.Name;
Console.WriteLine("В потоке " + currentName);
}
} while(stop == false && Count < 1000000000);
stop = true;
Console.WriteLine("Поток " + Thrd.Name + " завершен.");
}
}
class PriorityDemo {
static void Main() {
MyThread mt1 = new MyThread("с высоким приоритетом");
MyThread mt2 = new MyThread("с низким приоритетом");
// Установить приоритеты для потоков.
mt1.Thrd.Priority = ThreadPriority.AboveNormal;
mt2.Thrd.Priority = ThreadPriority.BelowNormal;
// Начать потоки,
mt1.Thrd.Start();
mt2.Thrd.Start() ;
mt1.Thrd.Join();
mt2.Thrd.Join();
Console.WriteLine();
Console.WriteLine("Поток " + mt1.Thrd.Name +
" досчитал до " + mt1.Count);
Console.WriteLine("Поток " + mt2.Thrd.Name +
" досчитал до " + mt2.Count);
}
}
Вот к какому результату может привести выполнение этой программы.
Поток с высоким приоритетом начат.
В потоке с высоким приоритетом
Поток с низким приоритетом начат.
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
Поток с высоким приоритетом завершен.
Поток с низким приоритетом завершен.
Поток с высоким приоритетом досчитал до 1000000000
Поток с низким приоритетом досчитал до 23996334
Судя по результату, высокоприоритетный поток получил около 98% всего времени, которое было выделено для выполнения этой программы. Разумеется, конкретный результат может отличаться в зависимости от быстродействия ЦП и числа других задач, решаемых в системе, а также от используемой версии Windows.
Многопоточный код может вести себя по-разному в различных средах, поэтому никогда не следует полагаться на результаты его выполнения только в одной среде. Так, было бы ошибкой полагать, что низкоприоритетный поток из приведенного выше примера будет всегда выполняться лишь в течение небольшого периода времени до тех пор, пока не завершится высокоприоритетный поток. В другой среде высокоприоритетный поток может, например, завершиться еще до того, как низкоприоритетный поток выполнится хотя бы один раз.
Синхронизация
Когда используется несколько потоков, то иногда приходится координировать действия двух или более потоков. Процесс достижения такой координации называется синхронизацией. Самой распространенной причиной применения синхронизации служит необходимость разделять среди двух или более потоков общий ресурс, который может быть одновременно доступен только одному потоку. Например, когда в одном потоке выполняется запись информации в файл, второму потоку должно быть запрещено делать это в тот же самый момент времени. Синхронизация требуется и в том случае, если один поток ожидает событие, вызываемое другим потоком. В подобной ситуации требуются какие-то средства, позволяющие приостановить один из потоков до тех пор, пока не произойдет событие в другом потоке. После этого ожидающий поток может возобновить свое выполнение.
В основу синхронизации положено понятие блокировки, посредством которой организуется управление доступом к кодовому блоку в объекте. Когда объект заблокирован одним потоком, остальные потоки не могут получить доступ к заблокированному кодовому блоку. Когда же блокировка снимается одним потоком, объект становится доступным для использования в другом потоке.
Средство блокировки встроено в язык С#. Благодаря этому все объекты могут быть синхронизированы. Синхронизация организуется с помощью ключевого слова lock. Она была предусмотрена в C# с самого начала, и поэтому пользоваться ею намного проще, чем кажется на первый взгляд. В действительности синхронизация объектов во многих программах на C# происходит практически незаметно.
Ниже приведена общая форма блокировки:
lock(lockObj) {
// синхронизируемые операторы
}
где lockObj обозначает ссылку на синхронизируемый объект. Если же требуется синхронизировать только один оператор, то фигурные скобки не нужны. Оператор lock гарантирует, что фрагмент кода, защищенный блокировкой для данного объекта, будет использоваться только в потоке, получающем эту блокировку. А все остальные потоки блокируются до тех пор, пока блокировка не будет снята. Блокировка снимается по завершении защищаемого ею фрагмента кода.
Блокируемым считается такой объект, который представляет синхронизируемый ресурс. В некоторых случаях им оказывается экземпляр самого ресурса или же произвольный экземпляр объекта, используемого для синхронизации. Следует, однако, иметь в виду, что блокируемый объект не должен быть общедоступным, так как в противном случае он может быть заблокирован из другого, неконтролируемого в программе фрагмента кода и в дальнейшем вообще не разблокируется. В прошлом для блокировки объектов очень часто применялась конструкция lock(this). Но она пригодна только в том случае, если this является ссылкой на закрытый объект. В связи с возможными программными и концептуальными ошибками, к которым может привести конструкция lock(this), применять ее больше не рекомендуется. Вместо нее лучше создать закрытый объект, чтобы затем заблокировать его. Именно такой подход принят в примерах программ, приведенных далее в этой главе. Но в унаследованном коде C# могут быть обнаружены примеры применения конструкции lock(this). В одних случаях такой код оказывается безопасным, а в других — требует изменений во избежание серьезных осложнений при его выполнении.
В приведенной ниже программе синхронизация демонстрируется на примере управления доступом к методу SumIt(), суммирующему элементы целочисленного массива.
// Использовать блокировку для синхронизации доступа к объекту.
using System;
using System.Threading;
class SumArray {
int sum;
object lockOn = new object(); // закрытый объект, доступный
// для последующей блокировки
public int SumIt(int[] nums) {
lock(lockOn) { // заблокировать весь метод
sum =0; // установить исходное значение суммы
for(int i=0; i < nums.Length; i++) {
sum += nums[i];
Console.WriteLine("Текущая сумма для потока " +
Thread.CurrentThread.Name + " равна " +
sum);
Thread.Sleep(10); // разрешить переключение задач
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- ИНФОРМАЦИОННАЯ ТЕХНОЛОГИЯ. РУКОВОДСТВО ПО УПРАВЛЕНИЮ ДОКУМЕНТИРОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ - ГОССТАНДАРТ РОССИИ - Программирование
- Управление исходными текстами. Часть 1. Краткое руководство по CVS - Илья Рыженков - Программирование
- Гибкое управление проектами и продуктами - Борис Вольфсон - Программирование
- Каждому проекту своя методология - Алистэр Коуберн - Программирование
- Разработка ядра Linux - Роберт Лав - Программирование
- Как спроектировать современный сайт - Чои Вин - Программирование
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование