Lock-Hierarchien sicherstellen

07/12/2015 - 18:16 von Stefan Reuther | Report spam
Hallo,

ich/wir ("mein Team") habe hier ein größeres multithreaded Programm.

Ein Problem, das bei multithreaded auftritt, ist, dass man in einem
Thread Dinge tut, die man nicht tun sollte. Z.B. sollte man im UI-Thread
kein I/O machen, damit die UI nicht hàngt. Das Problem haben wir in den
Griff bekommen, indem wir Funktionen mit Zusatzargumenten annotiert haben:
void functionThatDoesIO(WorkerContext&, int actualArg);
'WorkerContext' ist einfach eine leere Klasse. Wer die Funktion aufrufen
will, muss ein Objekt dieses Typs pràsentieren. Das gibt dem Entwickler
ein deutliches Signal, dass er diese Funktion nicht aufrufen soll, wenn
er nur ein 'UIContext&' hat. Über Vererbung der Kontext-Klassen kann man
einfach erreichen, dass eine Funktion in mehreren Kontexten aufgerufen
werden kann.
class AnyContext { };
class WorkerContext : public AnyContext { };
class UIContext : public AnyContext { };
void a(AnyContext&);
void b(UIContext& c) { a(c); }
Macht man was falsch, geht's nicht durch den Compiler. Prima.

Das war die Vorrede.

Aktuell beißt uns ab und an die Verletzung von Locking-Hierarchien
(Deadlock). Da grübel ich nun seit geraumer Zeit, ob man dem müden
überarbeiteten Entwickler hier auf àhnliche Weise eine Hilfestellung
geben kann: "irgendwie" die Mutexe mit einem zusàtzlichen Typ
annotieren, und eine Funktion kann den Mutex nur locken, wenn sie ein
Objekt des entsprechenden Typs pràsentiert. Skizze:
template<typename T> class Mutex {
public:
void lock(T&);
void unlock();
};

struct InnerContext { };
struct OuterContext : public InnerContext { };
Mutex<InnerContext> innerMutex;
Mutex<OuterContext> outerMutex:

void innerFunction(InnerContext& c) {
innerMutex.lock(c);
// outerFunction(c); // geht nicht durch den Compiler
// outerMutex.lock(c); // geht nicht durch den Compiler
innerMutex.unlock();
}
void outerFunction(OuterContext& c) {
outerMutex.lock(c);
innerFunction(c); // geht
outerMutex.unlock();
}
Soweit, so gut.

Problematisch ist, dass die Software kein monolithischer Klops ist,
sondern aus mehreren unabhàngigen Teilen besteht. Eine Mutexhierarchie
kann sich also über mehrere Ebenen erstrecken. So mal als Beispiel:
- Mutex der Applikation
- àußerer Mutex des Frameworks
- Mutex in der Applikation im Callback
- innerer Mutex des Frameworks
Die Mutexhierarchie (...Context-Klassen) müsste man also irgendwie in
der Applikation definieren. Dazu müsste man im wesentlichen alle Teile
des Frameworks vertemplatisieren. Das ist doof.

Gibt es eine andere Möglichkeit, auf diese Weise Lockinghierarchien vom
Compiler prüfen zu lassen?

Gibt es andere Herangehensweisen?


Stefan
 

Lesen sie die antworten

#1 ram
07/12/2015 - 20:00 | Warnen spam
Stefan Reuther writes:
Aktuell beißt uns ab und an die Verletzung von Locking-Hierarchien
(Deadlock).



(Obwohl ich jetzt hier antworte, sollte ich voranstellen,
daß ich auf dem Gebiet des Multithreading nur wenig
Erfahrunge habe, also kein Experte dafür bin.)

Es ist praktisch unmöglich, realistische Programme mit
Multithreading so zu schreiben, daß man einigermaßen sicher
sein kann, daß im Betrieb keine Probleme auftreten. Da gibt
es Berichte von Programmen, bei denen dies erstmal nach
mehreren Monaten bei Kunden auftrat, obwohl diese von
Experten für Multithreading geschrieben wurden. Leider
finde ich diese Web-Seite nicht mehr. Hier aber eine andere:

»non-trivial multi-threaded programs are
incomprehensible to humans.«

www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf

Die Autoren des Java-Werkzeugkastens »SubArctic« schrieben:

»It is our basic belief that extreme caution is
warranted when designing and building multi-threaded
applications, particularly those which have a GUI
component. Use of threads can be very deceptive. In many
cases they appear to greatly simplify programming by
allowing design in terms of simple autonomous entities
focused on a single task. In fact in some cases they do
simplify design and coding. However, in almost all cases
they also make debugging, testing, and maintenance
vastly more difficult and sometimes impossible. Neither
the training, experience, or actual practices of most
programmers, nor the tools we have to help us, are
designed to cope with the non-determinism. For example,
thorough testing (which is always difficult) becomes
nearly impossible when bugs are timing dependent.«

Gibt es andere Herangehensweisen?



Eventuell könnten futures einige Probleme vermeiden.

Eine andere Sache, mit der ich mich mal mehr praktisch
beschàftigen wollte, aber für die ich bisher noch keine
Zeit hatte: Alle Threads kommunizieren untereinander
ausschließlich über asynchrone Nachrichten(warteschlangen):

www.drdobbs.com/parallel/use-threads-correctly-isolation-asynch/215900465

Ähnliche fragen