
- •Multithreading - The Delphi Way. Многопоточность - как это делается в Дельфи.
- •Глава 8. Потокобезопасные классы в Дельфи и приоритеты 62
- •Глава 9. Семафоры. Управление потоками данных Взаимосвязь источник-приемник 70
- •Глава 10. Ввод/вывод и потоки данных: от блокировки к асинхронности и обратно 96
- •Глава 11. Синхронизаторы и события (Events). 121
- •Глава 12. Еще о возможностях синхронизации в Win32. 139
- •Глава 13. Использование потоков совместно с bde, Исключениями и dll 145
- •Глава 14. Проблемы, встретившаяся на практике, и их решение 163
- •Введение.
- •Посвящения.
- •Определения
- •Разделение времени
- •Для чего используют потоки?
- •Глава 2. Создание потока в Delphi. Предисловие с диаграммой.
- •Наш первый не vcl поток
- •Что именно делает эта программа?
- •Проблемы и сюрпризы.
- •Проблемы запуска.
- •Проблемы взаимодействия
- •Проблемы завершения
- •Глава 3. Основы синхронизации. Как разделять данные между потоками?
- •Атомарность при доступе к общим данным
- •Дополнительные проблемы с vcl
- •Многопроцессорные машины.
- •Решение для Delphi: tThread.Synchronize.
- •Как это работает? Что делает Synchronize?
- •Синхронизация для не-vcl потоков.
- •Глава 4. Простое разрушение потока Проблемы завершения, остановки и разрушения потоков
- •Досрочная остановка потока
- •Событие OnTerminate.
- •Контролируемая остановка потока - Подход 1.
- •Глава 5. Снова о разрушении потока. Тупик или зацикливание (Deadlock) Метод WaitFor
- •Контролируемое завершение потока - Подход 2
- •Введение в обработку сообщений и отложенное уведомление
- •WaitFor может вызвать долгую задержку
- •Вы заметили ошибку? WaitFor и Synchronize: зацикливание
- •Как избежать такого тупика
- •Глава 6. Снова о синхронизации: Критические секции и мьютексы Ограничения Synchronize
- •Критические секции
- •Что это все значит для Delphi-программиста?
- •На заметку
- •Могут ли данные пропасть или остаться недоступными в буфере?
- •Как насчет запоздавших сообщений (out of date)?
- •Проблемы Flow Control и неэффективность списка
- •Мьютексы
- •Глава 7. Программирование с использованием мьютексов. Управление конкуренцией Пора позаботиться о стиле?
- •Тупик из-за упорядочения мьютексов
- •Избавляемся от зацикливания потоков путем ожидания
- •Избавляемся от зацикливания, устанавливая упорядочение захвата мьютексов
- •Из огня да в полымя!
- •Избавляемся от зацикливания "ленивым способом", давая Win32 сделать это за нас
- •Атомарность составных операций - управление конкуренцией оптимистическим и пессимистическим образом
- •Оптимистическое управление
- •Пессимистическое управление
- •Избавляемся от недостатков в схеме блокировки
- •Еще не все ясно? Можно и попроще!
- •Глава 8. Потокобезопасные классы в Дельфи и приоритеты Для чего писать потокобезопасные классы?
- •Типы потокобезопасных классов.
- •Потокобезопасная инкапсуляция или наследники существующих классов
- •Классы управления потоками данных
- •Мониторы
- •Классы Interlock (взаимоблокировки)
- •Поддержка потоков в vcl
- •Руководство разработчика потокобезопасных классов
- •Управление приоритетами
- •Что такое приоритет? Как это делается в Win32
- •Какой приоритет дать моему потоку?
- •Глава 9. Семафоры. Управление потоками данных Взаимосвязь источник-приемник Семафоры
- •Счетчик больше единицы? "Не вполне критические" секции
- •Новое применение семафоров: управление потоками данных
- •Ограниченный буфер
- •Реализация ограниченного буфера в Delphi
- •Создание: Корректная инициализация счетчиков семафоров
- •Работа: правильные времена ожидания
- •Разрушение: Очистка
- •Разрушение: тонкости остаются
- •Доступ к дескрипторам синхронизации должен быть синхронизован!
- •Управление дескрипторами в Win32
- •Решение
- •Использование ограниченного буфера: пример
- •В завершение...
- •Глава 10. Ввод/вывод и потоки данных: от блокировки к асинхронности и обратно Отличия от потока vcl и разработка интерфейса I/o (ввода/вывода)
- •Реализация компонента преобразования блокировка-асинхронность
- •Добавление к ограниченному буферу операций просмотра
- •Создание двунаправленного ограниченного буфера
- •Детальное рассмотрение Блокировочно-Асинхронного Буфера (баб)
- •Создание баб
- •Разрушение баб
- •Пример программы с использованием баб
- •Мы достигли нашей цели!
- •Заметили утечку памяти?
- •Избавляемся от утечки
- •Проблемы просмотра
- •Промежуточный буфер.
- •Различные ограничения
- •Обратная сторона монеты: Потоковые буферы
- •Глава 11. Синхронизаторы и события (Events). Дополнительные механизмы синхронизации.
- •Как добиться оптимальной эффективности.
- •Простой mrews.
- •Важные моменты реализации
- •Пример использования простого mrews
- •Основные операции:
- •Введение в события (Events).
- •Моделирование событий с помощью семафоров.
- •Простой mrews с использованием событий.
- •Глава 12. Еще о возможностях синхронизации в Win32. Повышенная эффективность с помощью операций взаимоблокировки (interlocked)
- •Атомарность ниоткуда
- •Счетчики событий и секвенсоры
- •Реализация взаимного исключения
- •Ограниченный буфер с одним поставщиком данных и одним потребителем
- •Ограниченный буфер с произвольным числом поставщиков и потребителей
- •Другие возможности синхронизации Win32
- •Глава 13. Использование потоков совместно с bde, Исключениями и dll dll и многопроцессное программирование
- •Поток и пространство процесса. Однопоточная dll
- •Написание многопоточных dll
- •Подключение dll
- •Западня 1: Энкапсуляция функции точки входа в Delphi
- •Написание многопроцессорной dll
- •Глобально именованные объекты
- •Dll в деталях
- •Инициализация dll
- •Приложение с использованием dll
- •Западня 2: Контекст потока в фунциях точки входа
- •Обработка исключений
- •Глава 14. Проблемы, встретившаяся на практике, и их решение Проблема
- •Решение
- •Пайп dll и интерфейсные файлы
- •Потоки читателя и писателя
Мьютексы
Читатель может подумать, что я потратил столько времени на объяснение критических секций, и совсем забыл о мьютексах. Но это не так - просто мьютексы не представляют собой никаких новые концепций. Мьютекс работает точно так же, как и критическая секция. Единственное различие в реализациях Win32 - в том, что критическая секция ограничена использованием в пределах только одного процесса. Если у вас есть единая программа, которая использует несколько потоков, то критическая секция - легкий и удобный способ обеспечения ваших потребностей. Тем не менее, при написании DLL часто возможно использование DLL несколькими разными процессами одновременно. В этом случае вы должны использовать вместо критических секций мьютексы. Хотя Win32 API обеспечивает более обширный диапазон функций для работы с мьютексами и другими объектами синхронизации, чем будет рассмотрено нами, следующие функции аналогичны функциям, приведенным выше для критических секций:
CreateMutex / OpenMutex
CloseHandle
WaitForSingleObject(Ex)
ReleaseMutex
Эти функции хорошо документированы в справке Win32 API, и будут более детально обсуждаться позже.
[1] Протокол TCP выполняет также много других странных и удивительных функций, среди которых, например, копирование с потерей информации или оптимизация размеров окна так, чтобы поток данных соответствовал возможностям не только обеих машин, но также и связывающей их сети, минимизируя задержки и увеличивая пропускную способность. Он также содержит back-off алгоритмы для гарантии того, что несколько TCP-соединений могут разделять одно физическое соединение без монополизации физического ресурса одним из них.
Глава 7. Программирование с использованием мьютексов. Управление конкуренцией Пора позаботиться о стиле?
Большинство представленных до сих пор в этом руководстве примеров было довольно грубыми. При проектировании многократно используемых компонентов или структуры большого многопоточного приложения, метод "полета вслепую" не подходит. Разработчик приложений или компонентов должен создавать классы со встроенными средствами обеспечения потокобезопасности, то есть классы, к которым возможен доступ из других потоков, содержащие подходящие внутренние механизмы, гарантирующие сохранность данных. Для этого разработчик компонентов должен позаботиться о решении некоторых проблем, которые возникают при использовании мьютексов в очень сложных приложениях. Если вы впервые пытаетесь написать потокобезопасный класс, не откладывайте из-за кажущейся сложности изучение вопросов, рассматриваемых в этой главе. Довольно часто может быть принято упрощенное решение, которое ценой некоторой эффективности позволяет избежать многих упомянутых здесь проблем. Заметьте, что решения, в которых упоминаются мьютексы, ообычно так же хорошо применимы и к критическим секциям. Я для краткости не буду каждый раз это отмечать.
Тупик из-за упорядочения мьютексов
Если в программе имеется несколько мьютексов, то бывает нетрудно ввести ее в тупик неправильным кодом синхронизации. Наиболее часто это происходит, если существует циклическая зависимость порядка, в котором захватываются мьютексы. В академической литературе это часто называют проблемой трапезы философов. Как мы видели ранее, критерий возникновения зацикливания состоит в том, что потоки ждут, пока другой поток освободит объект синхронизации. Простейший пример - для двух потоков, один из которых захватывает мьютекс A до захвата мьютекса B, а другой захватывает мьютекс B до захвата мьютекса A.
Конечно, вполне возможно получить зацикливание программы и более сложным образом, с циклической цепочкой зависимостей, как показано ниже для четырех потоков и четырех мьютексов от A до D.
Очевидно, что подобные ситуации недопустимы в большинстве приложений. Существует несколько способов решения этой задачи и методы снятия проблем с такими зависимостями, что и позволяет избавиться от зацикливания.