Threadsicherheit

Hallo Experten,

ich programmiere gerade eine Anwendung mit mehreren Klassen. Methoden der Klassen A,B und C greifen gleichzeitig auf eine Methode der Klasse D zu. Diese Methode setzt einen Counter immer um 1 hoch, wenn diese aufgerufen wird.

Jetzt möchte ich eine Threadsicherheit einbauen, damit nur sequenziell auf die Variable zugegriffen werden darf. Dabei ist es wichtig zu wissen, dass die Methoden alle auch mehrfach gleichzeitig aufgerufen werden können (da mehrere auf mein aspx-Formular gleichzeitig zugreifen wollen, um die Methoden aufzurufen). Methode A der Klasse A kann z.B. 5x gleichzeitig von einem User aufgerufen werden, Methode B der Klasse B n-mal etc…

Hat das von euch schonmal einer gemacht, und wenn ja,wie? Ich habe etwas von [MethodImpl(MethodImplOptions.Synchronized)] gelesen, kann mir aber noch nicht richtig vorstellen, wie ich das in meinem Fall anwenden soll.

Vielen Dank im Voraus für eure Bemühungen.

Hallo!
Normalerweise stellt man den gegenseitigen Ausschluß beim Zugriff auf gemeinsame Ressourcen durch lock() sicher.
Beispiel:

private object \_lockObj = new object();
private int \_counter;
public void ThreadSafeIncrementCounter()
{
 lock (\_lockObj)
 {
 \_counter++;
 }
}

Statt eines separat angelegten Objekts für lock könnte man zwar auch lock(this) verwenden, das kann aber negative Auswirkungen auf die Performance haben, wenn es mehrere derart gesicherte Bereiche in der Klasse gibt.

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo!
Normalerweise stellt man den gegenseitigen Ausschluß beim
Zugriff auf gemeinsame Ressourcen durch lock() sicher.
Beispiel:stuck_out_tongue:rivate object _lockObj = new object();
private int _counter;
public void ThreadSafeIncrementCounter()
{
lock (_lockObj)
{
_counter++;
}
}

Statt eines separat angelegten Objekts für lock könnte man
zwar auch lock(this)verwenden, das kann
aber negative Auswirkungen auf die Performance haben, wenn es
mehrere derart gesicherte Bereiche in der Klasse gibt.

Gruß,
Martin

Hallo Martin,

danke für die schnelle Antwort! Warum kommt es dann zu Performanceproblemen? Ich habe im Openbook von Galileo Computing gelesen, dass lock(this) eine Sicherheitslücke darstellen soll.

Ich habe 3 Methoden. Eine Methode getConnectionCount, die nur die aktuelle Zahl der verwendeten Verbindungen zur Datenbank zurückgibt, die Methode setCount, die den Counter bei einer neuen Verbindung hochsetzt und die Methode canConnect, die sicherstellt, ob eine Verbindung zu ermöglichen ist.

Meinst du, dass ich diese drei Methoden vollständig mit lock(this) absichern könnte, ohne Performance einzubüßen?

Und muss ich alle anderen Klassen, die gleichzeitig aufgerufen werden können auch locken, wenn jemand darauf zugreift?

Tut mir Leid, dass ich so schreibfreudig bin :smile:

Hallo nochmal!

Würde folgendes Beispiel eine stabile Synchronisation gewährleisten?


int _connectionCounter=0;

public void SetConnectionCounter()
{
lock (this)
{
canConnect(); // darf sich die andere Klasse mit der Datenbank verbinden?
_connectionCounter++; // eine neue Verbindung als benutzt kennzeichnen
}
return theOldName;
}

public void degrementCounter()
{
lock (this)
{
_connectionCounter–; // Eine Datenbankverbindung wurde wieder frei
}
return theOldName;
}

public bool canConnect()
{
if (_connectionCounter

Hallo nochmal!
Bis auf die überflüssigen/falschen „return theOldName;“ geht das m.M. nach (fast) so. Du solltest aber auch canConnect() absichern.
Folgender Fall: canConnect() wird von außen im Rahmen von Thread T1 aufgerufen (ist schließlich public) und kommt bis zur Überprüfung, ob _connectionCounter Sicherheitsrisiko sein soll, entzieht sich mir - schreiben die Leute von Galileo auch, warum?
Die allgemeine Möglichkeit der Performancebeeinträchtigung bei lock(this) beruht darauf, dass es halt immer nur ein this gibt und anhand dieses Objekts synchronisiert wird, auch wenn die zu synchronisierenden Bereiche auf komplett unabhängigen Daten arbeiten.
Beispiel:

int A,B;
void IncA()
{
 lock(this)
 {
 A++;
 }
}
void IncB()
{
 lock(this)
 {
 B++;
 }
}

stellt sicher, dass A und B threadsicher inkrementiert werden, aber eben nicht gleichzeitig. Während this in IncA gesperrt ist, kann der Block in IncB nicht durchlaufen werden.
Mit zwei separaten Lock-Objekten ginge das.

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Martin,

danke nochmal für deine Antwort. ein Kollege hat nun gemeint, dass ich versuchen soll ein Singleton zu schreiben. Dieses Singleton soll die Schnittstelle zur Datenbank übernehmen und auch die verfügbaren Datenbankthreads holen. Es sollen also alle 3 Methoden, die verschiedene Datenbankabfragen realisieren an ein Singleton gehen, sich von dort eine Connection aus dem Connection Pool holen und dann damit die jeweilige Verbindung bis zum Schließen bereitstellen. Hast du das schon einmal gemacht? Ein paar Dinge dazu habe ich bereits gefunden.

Danke für die schnelle Antwort!

Hi nochmal!
Vielleicht sollten wir hierfür jetzt einen neuen Thread aufmachen, weil das mit der ursprünglichen Frage nicht mehr viel zu tun hat.

Das Singleton-Pattern ist ja recht einfach, da findest Du bestimmt alles Nötige durch kurzes Googeln, genauso wie beim ConnectionPooling (z.B. http://www.c-sharpcorner.com/UploadFile/dsdaf/ConnPo…)

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]