Кто-то в комментах намекал, чтоб я написал что-нибудь о Питоне. Ну вот, дождались.
Понадобился мне read-write lock. Что это такое? Расскажу. Поскольку не все тут программисты, начну от печки.
Программы бывают многопоточные (multi-threaded), то есть несколько действий могут выполняться как-бы-одновременно. Данные программы доступны из всех потоков; при этом программист, вообще говоря, не знает, когда происходит переключение между выполняемыми действиями. Представьте, например, такую функцию:
def increase():
global x
y = x
... # сделать много чего
x = y + 1
Эта функция берёт глобальную переменную х, запоминает её значение, долго что-то делает, потом записывает в х значение, на единицу большее. Представьте, что в двух потоках стартует эта функция, назовём их копия1
и копия2
.
- копия1 стартует и читает значение х — допустим, оно равно 5. Начинает делать много чего
- переключение потока
- копия2 стартует и читает значение х = 5. Начинает делать много чего
- переключение потока
- копия1 доделывает много чего, записывает в х значение 6
- переключение потока
- копия2 доделывает много чего, записывает в х значение 6
В результате после двух вызовов increase() значение х увеличилось только на единицу. Вряд ли программист будет доволен.
Это и есть Великая Проблема Многопоточного Программирования.
Есть такие сущности, которые позволяют решать эту проблему. Простейшая штука — mutex, он же lock (эй, спецы, есть какая-то тонкая семантическая разница?..). Lock можно взять (acquire) и отпустить (release). Если в одном потоке я его захватил, то другой поток не сможет его взять, пока я не отпущу его в первом, и будет вынужден ждать. Так что если создать какой-то lock и в начале нашей функции increase() его захватывать, а в конце отпускать, то переменная х будет увеличиваться, как надо. (Фактически, два экземпляра increase() не будут выполняться параллельно.)
Да, а read-write lock — это такой lock, который позволяет большому (неограниченному) количеству потоков захватывать себя «на чтение» (реальный смысл зависит от программиста),и только одному — «на запись» (разумеется, нельзя начинать писать, пока кто-то читает, и нельзя читать, пока кто-то пишет). Проблема в том, что простой lock, так же как семафор и кое-что ещё, входит в стандартную библиотеку Питона, а вот R/W Lock, к сожалению, нет.
Ваня поделился со мной одной из реализаций, которая используется в Django, и она уже довольно сильно взорвала мой мозг (чего стоит одна идея инициализировать семафор залоченным состоянием!), но когда мне показалось, что я начинаю понимать, как она работает, чёрт дёрнул меня поискать её в Сети (Ваня прислал мне её файлом). Выяснилось, что RWLock не писал только ленивый, и каждый следующий вариант страшнее предыдущего.
Говорят, чтобы научиться хорошо программировать, надо читать много чужого кода.
Ещё говорят, не сломалось — не чини. Поэтому я пока обойдусь первым вариантом, джанговским. Было бы любопытно сравнить различные варианты, но это может полдня занять, увы. А он уже кончился.
Неприятно, что при постулируемом «batteries included» такой востребованной штуки нет в стандартной библиотеке. Может, добавят?
Хорошо, когда есть, к кому пойти за советом. Знания передаются из рук в руки.
Многопоточное программирование требует аккуратности.
Вот и всё, что я могу сказать о программировании на сегодня. Рассуждать о судьбах блогосферы как-то проще.
One Response »