Wie mache ich einen 2.7 Python Context Manager threadsafe

9

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:

%Vor%

Wie kann ich diesen Kontextmanager threadsicher machen, so dass er in jedem Python-Thread konsistent verwendet wird, auch wenn er einspringt?

    
Paul Whipp 29.10.2015, 06:41
quelle

2 Antworten

5

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):

%Vor%

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.

    
BrenBarn 02.11.2015, 05:03
quelle
-1

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%     
Nizam Mohamed 06.11.2015 16:51
quelle