Саммерфилд - Программирование на Python 3 (1077331), страница 112
Текст из файла (страница 112)
ВатзцгзапецоввтНзпб1ег, а для обеспечения низкоуровневого доступа можно было бы наследовать класс воскетвегчег. ВавеВеооевтнвпо1ег. Словарь ВецоевтНапб1ег. Св гя — это атрибут класса, который добавляется в функции аатп(). Он хранит все регистрационные данные. Добавление дополнительных атрибутов в объекты (такие как классы или экземпляры) может производиться за пределами объявления класса (в данном случае — в функции ав1п()) без каких-либо ограничений (при условии, что объект имеет атрибут О(от ), что может быть очень удобно. Так как заранее известно, что класс зависит от этого атрибута, можно было бы добавить строку Сага = Нопв в объявление, чтобы зафиксировать существование переменной.
Почти каждый метод, обрабатывающий запросы, должен иметь доступ к словарю Сага, но нам следует гарантировать, что обращение к нему никогда не будет происходить из двух методов (из двух разных потоков) одновременно, в противном случае это может привести к повреждению словаря или к аварийному завершению программы. Чтобы избежать этих неприятностей, мы предусмотрели блокировку, оформленную в виде атрибута класса. С ее помощью мы сможем гарантировать, что в каждый конкретный момент времени только один поток будет иметь доступ к словарю.' (Принципы разработки многопоточных программ, включая использование блокировок, рассматриваются в главе 9.) Словарь Св11 — это еще один атрибут класса. Каждый ключ это словаря является именем операции, которую может выполнять сервер, а каждое значение — это функция, выполняющая соответствующую операцию.
Мы не можем использовать методы непосредственно, как это было сделано с функциями в словаре, обслуживающем меню в клиентской программе, потому что на уровне класса отсутствует ссылка ве1Г. Чтобы решить эту проблему, мы использовали функции-обертки, которые получают значение ссылки яе1Г в момент вызова и затем вызывают соответствующий метод, передавая ему ссылку ве1Г и любые другие аргументы. Как вариант, словарь Св11 можно было бы создать по. еле определения всех методов. Это позволило бы создать такие записи, как ОЕТ САВ ОЕТА1ЕЯ=цет саг Оетз)1в, и интерпретатор Ру(Ьоп сумел бы Глобальная блокировка интерпретатора (О!оЬа1 1пбегргевег Глобальная Ессеи 011.) гарантирует синхронизированный доступ к ело. ваРю Сзго, но, как отмечалось Ранее, мы не можем полагать- Вн отп зтз оя на нее, так как она является особенностью реализации СРуВЬоп.
Сервер ТСР отыскать метод 0е! свг бе!а!18(), так как словарь создается после того, как все методы были определены. Но мы предпочли использовать первый вариант, так как он более явный и не зависит от того, в каком месте класса находится определение словаря. Несмотря на то, что после создания класса словарь Св11 Глоб»явная всегда будет использоваться только для чтения, тем не бпокяровм менее, учитывая, что словарь является изменяемым 6(Остр.478 объектом, мы решили обеспечить дополнительный уровень безопасности и определили блокировку, которая гарантирует, что в каждый конкретный момент времени доступ к нему будет иметь только один поток. (Она также не является обязательной из-за глобальной блокировки, присутствующей в реализации Сруб]топ.] бег Папб1е(ве1().
8!вевтгоо! = в!гост.в!гост( Ч 1") в!ге бата = ве1(.гт!1е.гввб(в!зев!гост.в!ге) в!ве = 8ыевтгоо!.опрасК(в!ге бата)[0] бата = ртсК1е. 1оабв(ве1Г, гт!1е, гевб(в!ге)) тгу: и!то ве1Г. Са11!осК; топот!оп = вв!г.са11[бата[0]] гер1у = топот!оп(ве1(, *бэта[т:]) ехсер! Р!птвл: гетогп батя = рток1е.боэрв(гер1у, 3) ве1(.тгг!1е.ттгтте(8тгевтгост.раск( 1еп(бата))) ве)г.кг!)е.кг!те(батя) Всякий раз, когда клиент выполняет запрос, создается новый поток с новым экземпляром класса НеооевтНапб1ег, после чего вызывается метод Напб1е() экземпляра. Внутри этого метода данные, полученные от клиента, можно прочитать с помощью объекта файла ве1(.
ГГ!1е, а отправка данных клиенту может быть выполнена с помощью объекта файла ве1[.т»Г!1е. Оба эти объекта предоставляются модулем восКетвегчег уже открытыми и готовыми к использованию. Объект типа в!гос!. 8! гост — это целочисленный счетчик байтов, который необходим, чтобы прочитать формат»длина плюс данные», используемый при обмене данными между клиентами и сервером. Сначала выполняется чтение первых четырех байтов и распаковывание их в целое число в!ве, чтобы узнать размер законсервированного объекта, отправленного клиентом. Затем выполняется чтение вше байтов и распаковывание их в переменную баСа. Операция чтения блокирует дальнейшее выполнение, пока данные не будут прочитаны.
В данном случае известно, что данные всегда поступают в виде кортежа, первый элемент которого определяет выполняемую операцию, Глаза 10. Сети а остальные элементы являются параметрами этой операции, потому что зто определено протоколом, установленным для клиентов. Внутри блока тгу мы получаем лямбда-функцию, соответствующую запрошенной операции. Доступ к словарю Са11 выполняется под защитой блокировки, хотя вполне возможно, что мы проявляем излишнюю осторожность.
Как обычно, мы стараемся выполнять как можно меньше действий в области видимости блокировки — в данном случае мы всего лишь отыскиваем в словаре ссылку на требуемую функцию. Как только функция будет получена, она тут же вызывается и ей передаются в виде первого аргумента — ссылка ве1( и в виде последующих аргументов — остальное содержимое кортежа дата. Здесь производится вызов функции, поэтому ссылка ве1Т не передается ей интерпретатором. Это не имеет большого значения, так как мы сами передаем ссылку ве1(, а внутри лямбда-функции полученная ссылка ве1( используется для вызова метода обычным способом. В результате производится вызов метода ве1(.аетлогг(*г)ата(1: ) ), где ветлргг — это метод, соответствующий операции, указанной в г)ага(0). Если выбрана операция остановки сервера, в методе влитсоил( ) возбуждается наше собственное исключение Ртл1яа — в этой ситуации известно, что клиент не ожидает ответа, поэтому мы просто можем вернуть управление.
Но для остальных операций выполняется консервирование (с использованием протокола 3) объекта с результатами работы метода, соответствующего запрошенной операции, и запись — сначала размера законсервированного объекта, а затем самого объекта с данными. ает двт саг Евтв(1в(ве!т, 1(серве): иття ве1Г. СагвСос(с саг = сору. сору(ве)г.свгв.цет(!тселве, мопв)) тт свг тв лат Моля; гетягл (тгие, саг.веатв, саг.з11евце, саг.вггвег) ге(игл (Ра1ве, "тпгв 1тселве тв лот гецтвтегес") Этот метод начинается с того, что пытается получить блокировку для доступа к данным и блокируется, пока не получит ее. Затем с помощью метода О(ст.
цет(), которому вторым аргументом передается значение Моле, он получает сведения об автомобиле с указанным номером или Моле. Информация тут же копируется, и инструкция и11П завершается. Этим обеспечивается максимально короткое время, в течение которого блокировка будет занята. Операция чтения не изменяет словарь, но, так как мы имеем дело с изменяемой коллекцией, вполне возможно, что какой-нибудь другой поток в то же самое время попробует изменить словарь — использование блокировки устраняет такую опасность. Теперь, когда мы находимся вне области действия блокировки, у нас имеется объект с копией информации об автомобиле (или Моле), с которой можно продолжить работу, не блокируя другие потоки.
Сервер ТСР 503 Все методы, выполняющие операции с регистрационной информацией, возвращают кортеж, первым элементом которого является логический флаг — признак успешного выполнения операции, а количество и значения остальных элементов зависят от конкретного метода. Ни один из этих методов даже понятия не имеет и не делает никаких предположений о данных, возвращаемых клиенту, кроме того, что эти данные представляют собой»кортеж, первым элементом которого является логический флаг», так как все сетевые взаимодействия сосредоточены в методе Ьапо1е(). Овг спапде а!!ваде(ве11, 1!свпве, з!1еаде); !Т а!1еаде < 0: ге!огп (Ра1ве, "Саппо! ве! а пода!дче з!1ваде") е!!Ь ве1Г,СагвсосК: саг = ве11.Сага.де!( 1дсепве, аопе) !Т саг !в пег зопе: !Г саг.э!1еаде < ю1ааде: саг.э!1еаде = ю1еаде ге!огп (Тгое, аопе) га!Ьгп (еа1ва, "саппоро ишс гье ооозе!ег ьаси') гесогп (Ра1ве, "тмв 1!сепве ьв по! гед!в!егес") В этом методе мы можем выполнить одну проверку, не прибегая к использованию блокировки.
Но если величина пробега неотрицательна, необходимо приобрести блокировку и получить ссылку на объект с информацией о соответствующем автомобиле, и если требуемый объект существует (например, если указан верный номер автомобиля), необходимо остаться в области действия блокировки, чтобы изменить величину пробега в соответствии с запросом клиента, в противном случае вернуть кортеж с сообщением об ошибке. Если для запрошенного номера автомобиля в словаре отсутствует информация (саг == ))опе), метод выходит из области видимости инструкции»гв!Ь и возвращает кортеж с сообщением об ошибке.