- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
• Фоновые потоки (иногда называемые демонами) рассматриваются средой CLR, как возобновляемые ветви выполнения, которыми можно пренебречь в любой момент времени (даже при выполнении ими своих задач). Поэтому, когда все приоритетные потоки завершаются, все фоновые потоки будут завершены автоматически в результате выгрузки домена приложения.
Важно понять, что понятия приоритетного и фонового потоков – это не синонимы понятий первичного и рабочего потока. По умолчанию каждый поток, создаваемый с помощью метода Thread.Start(), автоматически оказывается приоритетным потоком. А это значит, что домен приложения не будет выгружен до тех пор, пока в нем все потоки не завершат свою работу. В большинстве случаев это будет именно тем поведением, которое требуется.
Но предположим, что нам нужно вызвать Printer.PrintNumbers() во вторичном потоке, который должен действовать, как фоновый поток. Это означает, что для метода, на который указывает тип Thread (посредством делегата ThreadStart или ParameterizedThreadStart), должна допускаться возможность безболезненного его завершения, как только все приоритетные потоки закончат свою работу. Для настройки такого потока достаточно установить значение true (истина) для свойства IsBackground.
static void Main(string[] args) {
Printer p = new Printer();
Thread bgroundThread = new Thread(new ThreadStart(p.PrintNumbers));
bgroundThread.IsBackground = true;
bgroundThread.Start();
}
Обратите внимание на то, что метод Main() здесь не вызывает Console.ReadLine(), чтобы гарантировать присутствие консоли на экране до нажатия клавиши «Enter». Поэтому при выполнении этого приложения оно сразу же прекратит свою работу, так как объект Thread сконфигурирован для работы в фоновом потоке. С началом работы метода Main() создается приоритетный первичный поток, поэтому, как только выполнение программной логики Main() завершится, домен приложения будет выгружен, и это произойдет до того, как вторичный поток завершит свою работу. Однако, закомментировав строку, в которой устанавливается свойство IsBackground, вы обнаружите, что на консоль выводятся все числа, поскольку для того, чтобы домен приложения будет выгружен из содержащего его процесса, все приоритетные потоки должны завершить свою работу.
Обычно конфигурация потока для выполнения в фоновом режиме может быть полезна тогда, когда соответствующий рабочий поток выполняет некритичные задания, которые оказываются не нужными после завершения выполнения главной задачи программы.
Исходный код. Проект BackgroundThread размещен в подкаталоге, соответствующем главе 14.
Проблема конкурентного доступа
До сих пор все многопоточные приложения, созданные вами при изучении материала этой главы, были устойчивыми в отношении потоков, поскольку в них соответствующие методы вызывались только одним объектом Thread. Конечно, некоторые из ваших приложений могут быть настолько же простыми, но большинство многопоточных приложений содержит очень много вторичных потоков. С учетом того, что все потоки в домене приложения могут претендовать на доступ к открытым данным приложения одновременно, представьте себе, что может случиться, если к одному и тому же элементу данных получит доступ множество потоков. Поскольку планировщик потоков может приостановить работу потока в любой момент времени, что будет, если поток А будет отстранен от выполнения своей работы на полпути до того, как он эту работу завершит? Поток В будет читать некорректные данные.
Чтобы проиллюстрировать проблему конкурентного доступа, давайте построим еще одно консольное приложение C#, которое мы назовем MultiThreadedPrinting, Это приложение будет использовать класс Printer, созданный нами ранее, но на этот раз метод PrintNumbers() "заставит" текущий поток делать паузы произвольной длительности в соответствии со случайно генерируемыми значениями.
public class Printer {
public void PrintNumbers() {
…
for (int i = 0; i ‹ 10; i++) {
Random r = new Random();
Thread.Sleep(1000 * r.Next(5));
Console.Write(i + ", ");
}
Console.WriteLine();
}
}
Метод Main() отвечает за создание массива из десяти объектов Thread с уникальными именами), каждый из который вызывает один и тот же экземпляр Printer.
class Program {
static void Main(string[] args) {
Console.WriteLine("***** Синхронизация потоков *****n");
Printer p = new Printer();
// Создание 10 потоков, указывающих на один и тот же метод
// одного и того же объекта.
Thread[] threads = new Thread[10];
for (int i = 0; i ‹ 10; i++) {
threads[i] =new Thread(new ThreadStart(p.PrintNumbers));
threads[i].Name = string.Format("Рабочий поток #{0}", i);
}
// Теперь старт каждого их них.
foreach (Thread t in threads) t.Start();
Console.ReadLine();
}
}
Перед тем как выполнить тестовый запуск программы, давайте обсудим cо-ответствующую проблему. Здесь первичный поток в рамках домена приложения порождает десять вторичных рабочих потоков. Каждому рабочему потоку дается указание вызвать метод PrintNumbers() одного и того же экземпляра Printer. Поскольку здесь не предпринято никаких мер по блокированию общедоступных ресурсов данного объекта (консоли), имеется большая вероятность того, что текущий поток будет приостановлен до того, как метод PrintNumbers() закончит вывод всех своих результатов. Вы не знаете точно, когда это случиться (и случится ли вообще), поэтому нужно быть готовым к непредвиденным результатам. Например, может получиться вывод, показанный на рис. 14.8.
Рис. 14.8. Конкуренция в действии, первая попытка
Выполните приложение еще несколько раз. На рис. 14.9 показана другая возможность вывода (ваши результаты, очевидно, тоже будут другими).
Рис. 14.9. Конкуренция в действии, вторая попытка
Ясно, что проблемы здесь действительно есть. Каждый поток дает указание объекту Printer печатать числовые данные, и планировщик потоков запускает выполнение этих потоков в фоновом режиме. В результате получается несогласованный вывод. В этом случае мы должны программно организовать синхронизованный доступ к совместно используемым ресурсам. Нетрудно догадаться, что в пространстве имен System.Threading есть целый ряд типов, имеющих отношение к синхронизации. А язык программирования C# предлагает специальное ключевое слово, как раз для решения задач синхронизации совместного доступа к данным в многопоточных приложениях.
Замечание. Если у вас не получается сгенерировать непредвиденный вывод, увеличьте число потоков с 10 до 100 (например) или добавьте в свою программу вызов Thread.Sleep(). В конце концов вы все равно столкнетесь с проблемой конкурентного доступа
Синхронизация с помощью ключевого слова lock в C#
Первой из возможностей, которую вы можете применить в C# для синхронизации доступа к совместно используемым ресурсам, является использование ключевого слова lock. Это ключевое слово позволяет определить контекст операторов, которые должны синхронизироваться между потоками. В результате входящие потоки не смогут прервать текущий поток, пока он выполняет свою работу. Ключевое слово lock требует, чтобы вы указали маркер (объектную ссылку), который потребуется потоку для входа в пределы контекста lock. При блокировке метода уровня экземпляра можно использовать просто ссылку на текущий тип.
// Использование текущего объекта в качестве маркера потока.
lock(this) {
// Весь программный код в этом контексте оказывается
// устойчивым в отношении потоков.
}
При внимательном изучении метода PrintNumbers() становится ясно, что совместно используемым ресурсом, за доступ к которому соперничают потоки, является окно консоли. Поместите в рамки соответствующего контекста блокировки все операторы взаимодействии с типом Console так, как показано ниже.
public void PrintNumbers() {
lock (this) {
// Вывод информации Thread.
Console.WriteLine("-› {0} выполняет PrintNumbers()", Thread.CurrentThread.Name);
// Вывод чисел.
Console.Write("Ваши числа": ");
for (int i = 0; i ‹ 10; i++) {
Random r = new Random();
Thread.Sleep(1000 * r.Next(5));
Console.Write(i + ", ");
}
Console.WriteLine();
}
}
Тем самым вы создадите метод, который позволит текущему потоку завершить выполнение своей задачи. Как только поток вступит в контекст блокировки, соответствующий маркер блокировки (в данном случае эта ссылка на текущий объект) станет недоступным другим потокам, пока блокировка не будет снята в результате выхода потока из контекста блокировки. Например, если маркер блокировки получает поток А, то другие потоки не смогут войти в контекст до тех пор, пока поток А не освободит маркер блокировки.