[Przepraszam za nieudolny tytuł; Nie mogłem wymyślić nic lepszego. Sugestie dotyczące lepszego tytułu są mile widziane.]

Chcę zaimplementować interfejs do HDF5, które obsługują współbieżność na poziomie wieloprocesowym przez blokowanie plików. Zamierzone środowisko dla tego modułu jest klaster Linux dostęp do dysku udostępnionego NFS. Celem jest włączenie jednoczesnego dostępu (NFS) do tego samego pliku przez wiele równoległych procesów działających na kilku różnych hostach.

Chciałbym być w stanie zaimplementować funkcjonalność blokowania przez klasa opakowania dla klasy h5py.File. (h5py Oferuje już obsługę współbieżności -level, ale podstawowa biblioteka HDF5 nie jest bezpieczna.)

Byłoby wspaniale, gdybym mógł zrobić coś w duchu

class LockedH5File(object):
    def __init__(self, path, ...):
        ...
        with h5py.File(path, 'r+') as h5handle:
            fcntl.flock(fcntl.LOCK_EX)
            yield h5handle
        # (method returns)

Zdaję sobie sprawę, że powyższy kod jest błędny, ale mam nadzieję, że przekazuje główną ideę: mianowicie, aby mieć wyrażenie LockedH5File('/path/to/file') dostarczyć otwarty uchwyt do kodu klienta, który może następnie wykonać różne dowolne operacje odczytu / zapisu . Gdy ten uchwyt wychodzi z zakresu, jego destruktor zamyka uchwyt, zwalniając tym samym blokadę.

Cel, który motywuje ten układ, jest dwukrotnie:

  1. Decouple Stworzenie uchwytu (przez kod biblioteki) z operacji, które są następnie wymagane na uchwycie (przez kod klienta) i

  2. Upewnij się, że uchwyt jest zamknięty, a blokada zwolniona, bez względu na to, co się dzieje podczas Wykonanie kodu interweniującego (np. wyjątki, nieobsługiwane Sygnały, błędy wewnętrzne Pythona).

Jak mogę osiągnąć ten efekt w Pythonie?

Dzięki!

5
kjo 22 listopad 2011, 00:15

3 odpowiedzi

Najlepsza odpowiedź

Aby wykonać tę pracę, Twoja klasa musi wdrożyć Protokół menedżera kontekstu. Alternatywnie, napisz funkcję generatora za pomocą contextlib.contextmanager Decorator.

Twoja klasa może się z grubsza wyglądać (szczegóły h5py są prawdopodobnie strasznie błędne):

class LockedH5File(object):
    def __init__(self, path, ...):
        self.h5file = h5py.File(path, 'r+')
    def __enter__(self):
        fcntl.flock(fcntl.LOCK_EX)
        return self.h5file
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.h5file.close()
6
Sven Marnach 21 listopad 2011, 20:19

Obiekty, które mogą być używane w with Instrukcje są nazywane menedżerami kontekstowymi; i wdrażają prosty interfejs. Muszą dostarczyć dwie metody, metodę {X1}}, która nie bierze argumentów i może zwrócić wszystko (co zostanie przypisane do zmiennej w części as), a metodą __exit__, która zajmuje trzy argumenty (które zostaną wypełnione w wyniku sys.exc_info()) i zwraca niezera, aby wskazać, że wyjątek był obsługiwany. Twój przykład prawdopodobnie będzie wyglądał:

class LockedH5File(object):
    def __init__(self, path, ...):
        self.path = path

    def __enter__(self):
        self.h5handle = h5handle = h5py.File(self.path, 'r+')
        fcntl.flock(fcntl.LOCK_EX)
        return h5handle

    def __exit__(self, exc_type, exc_info, exc_tb):
        self.h5handle.close()
8
SingleNegationElimination 21 listopad 2011, 20:25

Cóż, kontekst menedżer i with oświadczenie. Ogólnie rzecz biorąc, destruktory w Pythonie nie gwarantują uruchamiania w ogóle , więc nie powinieneś polegać na nich jako cokolwiek innego niż bezpieczne czyszczenie. Zapewnij metody __enter__ i __exit__ i użyj go jak

with LockedFile(...) as fp:
    # ...
2
Cat Plus Plus 21 listopad 2011, 20:21