КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Класс ReaderWriterLockОчень часто встречается проблема синхронизации потоков, известная как проблема «множественного чтения и одиночной записи» (multiple-reader/single-writer). Эта проблема возникает, когда произвольное число потоков пытается получить доступ к общему ресурсу. Одни потоки (записывающие) хотят изменить содержимое данных, другие (считывающие) — только считать данные. Синхронизация необходима по четырем причинам: 1. Когда один поток записывает данные, остальным потокам нельзя их изменять; 2. когда один поток записывает данные, остальным потокам нельзя их считывать; 3. когда один поток считывает данные, остальные потоки не должны изменять их; 4. когда один поток считывает данные, остальные потоки также могут считывать их.
FCL предоставляет класс ReaderWriterLock, инкапсулирующий эти четыре правила. Можно создать экземпляр этого класса и вызывать методы для установки и освобождения блокировки. У ReaderWriterLock нет статических членов, но есть несколько экземплярных методов и свойств. Обычно, подходя к этой теме, я описываю, как использовать такую блокировку в приложениях, но сейчас я этого сделать не могу, порекомендовать ее использовать могу только врагу. Как вы увидите далее, этот класс создает массу проблем. Во-первых, производительность только входа и выхода из блокировки ужасно низкая. Она неудовлетворительна, даже если нет конкурирующих за блокировку потоков. Мои измерения показали, что вход и выход в блокировку с использованием класса ReaderWriterLock выполняется примерно в пять раз медленнее, чем при помощи класса Monitor. Во-вторых, когда поток завершает запись, из всех ожидающих доступа потоков эта блокировка отдает приоритет считывающим, что приводит к исключительно медленной работе записывающих потоков. Обычно класс ReaderWriterLock используют, когда мало записывающих, но много считывающих потоков. В конце концов, если бы к ресурсу обращались только считывающие потоки, его не нужно было бы блокировать, а если обращаются только записывающие, то нужно использовать взаимоисключающую блокировку, например блокировку синхронизации (которой можно управлять при помощи класса Monitor). Так что если используется класс ReaderWriterLock и есть ожидающие своей очереди записывающие потоки, нужно отдавать приоритет им. Я знаю несколько человек, которые пытались использовать класс ReaderWriterLock, но из-за того, что он отдает приоритет считывающим потокам, записывающие потоки надолго блокировались и очень долго не могли выполнить свою задачу. В-третьих, класс ReaderWriterLock поддерживает рекурсию. То есть вошедший в блокировку и потом выходящий из нее поток должен быть одним и тем же потоком. Многие разработчики считают это преимуществом — я считаю это ошибкой. Все чаще возникают ситуации, когда один поток входит в блокировку и запускает операцию, обращающуюся к каким-то данным, а затем другой поток завершает эту операцию с данными и выходит из блокировки. Особенно часто это встречается в асинхронной модели программирования, описанной в главе 23. Блокировки, поддерживающие рекурсию, такие как ReaderWriterLock и Monitor, не поддерживают асинхронное программирование.
|