Многопоточность Python - PullRequest
1 голос
/ 24 июня 2011

У меня есть такой сценарий:

Веб-страница, созданная с помощью Zope / Plone и некоторого моего Python API.Есть веб-страница, называемая «а», которая с помощью метода python вызывает базу данных (Postgres) и возвращает некоторую информацию.На странице «а» вы можете изменить данные базы данных «в автономном режиме» (я имею в виду, что изменения записываются не в базу данных мгновенно, а во второй момент, когда вы нажимаете «сохранить» и вызываете метод Python API).Итак, представьте себе такой сценарий: пользователь по имени «Сэм» загружает страницу и начинает изменять данные.Тем временем пользователь, называемый «Сара», изменяет базу данных с помощью страницы «а», нажимая «сохранить».Теперь у Сэма нет фактических данных базы данных: он нажмет «сохранить» и перезапишет изменение данных Сары.

У меня будет предупреждение на моей странице в реальном времени.Я думал, что смогу сделать что-то вроде этого:

Сделайте AJAX-вызов, который не блокируется, и продолжайте рендеринг страницы.AJAX вызывает метод python, который создает поток, который выполняет бесконечный цикл (при условии «X»).Когда я записываю данные в базу данных, я вызываю функцию, которая изменит «условие X», останавливая поток и возвращаясь к AJAX.

Более того, я не могу заблокировать базу данных, потому что я должен дать свободный доступкаждому пользователю, который хочет изменить мою базу данных.

Моя проблема: как я могу определить поток Python?Я только что видел, что каждый метод класса, который наследуется от Thread, хочет использовать в качестве параметра «self».Кроме того, я должен вызывать поток, когда я получаю доступ к странице «а», и это будет где-то в коде (скажем, в «модуле потоков»), но вставки находятся в другом модуле.Итак, как я могу реализовать свою идею?

И если у кого-то есть альтернативная идея, скажите мне без проблем:)

1 Ответ

3 голосов
/ 24 июня 2011

Область проблемы, которую вы обсуждаете, обычно называется «Параллельность».Поскольку ваш метод будет предупреждать или блокировать обновление пользователем при изменении любого поля в целевом элементе, этот подход обычно называется «пессимистическим параллелизмом».Один из способов сделать это - отслеживать, как элемент выглядел при его выборе, и обновлять только в том случае, если версия базы данных выглядит точно так же, как выбранная вами версия или не обновлялась с определенного времени (поле метки времени может быть полезным).Вы также можете попробовать оптимистичный параллелизм, при котором вы проверяете, что поля, которые один пользователь обновил и сохраняет обратно в хранилище данных, не были обновлены другим пользователем.Оба эти метода наиболее просты, если вы выбираете библиотеку ORM, которая поддерживает параллелизм.

Моя любимая веб-библиотека на python - django, и вот вопрос о SO относительно той же ситуации, которую вы хотите решить: Django: Как я могу защитить от одновременного изменения записей базы данных .Я надеюсь, что это поможет.

Обработка параллелизма способом, который вы предлагаете, выполнима, но его следует избегать в большинстве ситуаций.Я делал это раньше, когда добавлял параллелизм к большой системе со сложными объектами, которые имели широкий спектр побочных эффектов и не имели единого доступа к данным (в течение времени существования системы было около 5 методов доступа к данным, это была красочная система).Это подверженный ошибкам и сложный способ обработки параллелизма (я думаю, что у меня было клиентское приложение, и я запустил поток наблюдателя после того, как пометил элементы как «проверенные» в таблице данных, описывающей тип и идентификатор объекта, пользователя, который его проверил, когда они его проверили, и как долго он был действителен, если клиент, который проверил объект, не смог проверить его, когда закончил).

Если вы настроены на использование не ORM и отображениесообщение пользователю, когда произошли изменения в элементе, попробуйте отключить последний обновленный столбец отметки времени и просто проверьте ajax-вызов, чтобы увидеть, больше ли время последнего обновления, чем было при первой загрузке элемента.Итак, если бы вы кодировали общий способ сделать это, вам просто потребуются имя таблицы, первичный ключ и временная метка.

метод веб-сервиса может выглядеть следующим образом:

def is_most_current(table_name, id):
    db = MySQLdb.connect(passwd="moonpie",db="thangs")
    c=db.cursor()
    c.execute("SELECT last_updated from %s where id = %s", (table_name, id))
    return c.fetchone()

Что касается многопоточных библиотек python, потоки python сбивают с толку и производят низкую производительность из-за проблем с глобальной блокировкой python, вы, возможно, захотите порождать новый процесс во многих случаях (многопроцессорная библиотека довольно эквивалентна и работает лучше в сценариях параллельной обработки),Что касается «я», то это питоническое соглашение для ссылки на экземпляр класса, с которым вы имеете дело, очень похоже на «это» в С-подобных языках.Вы можете легко определить поток, дав ему уникальное имя при его создании.См. многопроцессорная обработка или многопоточность документы для получения дополнительной информации.Если вы можете избежать многопоточности для этой проблемы, я рекомендую вам сделать это.

...