Ich habe eine große Python-Anwendung, die auf einem Django-Dienst läuft. Ich muss Erlaubnistests für bestimmte Operationen deaktivieren, also habe ich diesen Kontextmanager erstellt:
%Vor%Verschiedene Teile der Anwendung können dann die Tests mit dem Kontextmanager überlagern:
%Vor%Innerhalb des Do-Krams kann der obige Kontextmanager mehrfach in verschiedenen Funktionen verwendet werden. Die Verwendung des Zählers hält dies unter Kontrolle und es scheint gut zu funktionieren ... bis Threads involviert werden.
Sobald Threads beteiligt sind, wird der globale Kontextmanager wiederverwendet und als Ergebnis werden die Tests möglicherweise falsch überschrieben.
Hier ist ein einfacher Testfall - das funktioniert gut, wenn die thread.start_new_thread(do_id, ())
-Zeile durch ein einfaches do_it
ersetzt wird, aber wie gezeigt spektakulär ausfällt:
Wie kann ich diesen Kontextmanager threadsicher machen, so dass er in jedem Python-Thread konsistent verwendet wird, auch wenn er einspringt?
Hier ist eine Möglichkeit, scheint zu funktionieren: Machen Sie Ihre OverrideTests-Klasse zu einer Unterklasse von threading.local
. Zur Sicherheit sollten Sie dann die Oberklasse __init__
in Ihrem __init__
aufrufen (obwohl es scheint, auch wenn Sie nicht funktionieren):
Dann:
%Vor% Ich würde jedoch nicht darauf setzen, dass es in einer Art Eckfall nicht versagt, besonders wenn Ihre reale Anwendung komplexer ist als das Beispiel, das Sie hier gezeigt haben. Letztendlich sollten Sie Ihr Design wirklich überdenken. In deiner Frage konzentrierst du dich darauf, "den Kontext-Manager threadsicher zu machen". Aber das eigentliche Problem liegt nicht nur in Ihrem Kontextmanager, sondern auch in Ihrer Funktion ( stat
in Ihrem Beispiel). stat
basiert auf dem globalen Status (der globale override_tests
), der in einer Thread-Umgebung inhärent fragil ist.
threading.RLock
ist eine wiederkehrende Sperre, die mehrmals vom selben Thread erworben werden kann. Es unterstützt auch das Kontextverwaltungsprotokoll und kann daher mit with
statement verwendet werden.
Es hat ein Besitzerfeld, das den Thread darstellt, der die Sperre aktuell hält. Eine private Methode _is_owned
gibt an, ob der aufrufende Thread die Sperre besitzt. Der Besitzerwert kann verwendet werden, um festzustellen, ob die Sperre vom aktuellen Thread gehalten wird, was die Implementierung vereinfacht .
Es sind keine Zähler und kein lokaler Threadspeicher erforderlich. Wenn der aktuelle Thread nicht der Eigentümer ist, bedeutet dies, dass der aktuelle Thread die Sperre nicht blockiert, sodass er nicht überschrieben wird.
%Vor%Tags und Links python django multithreading python-2.7