Laden...

Entwurfsmuster Singleton und Schlüsselwort synchronized [und sollte lock(this) verwendet werden?]

Erstellt von peterchen72 vor 17 Jahren Letzter Beitrag vor 16 Jahren 6.796 Views
P
peterchen72 Themenstarter:in
66 Beiträge seit 2006
vor 17 Jahren
Entwurfsmuster Singleton und Schlüsselwort synchronized [und sollte lock(this) verwendet werden?]

Ich mache gerade das Buch "Entwurfsmuster von Kopf bis Fuß" durch. Kapitel 5 beschäftigt sich mit dem Singleton-Muster. Es gibt die einfache Variante mit einer statischen Variablen, die sofort erzeugt wird (und nicht erst bei Bedarf).


	public class SingletonStatisch
	{
		private static SingletonStatisch einzigeInstanz = new SingletonStatisch();

		private SingletonStatisch()
		{ 
		}

		public static SingletonStatisch getInstanz()
		{
			return einzigeInstanz;
		}

		public void Function()
		{
			Console.WriteLine("Ich bin eine vorzeitig erzeugte Instanz");
		}
	}

Eine andere Möglichkeit ist, die Instanz erst zu erzeugen, wenn sie auch benötigt wird. Um Threadsicherheit zu erzeugen wird in dem Buch das Schlüsselwort "synchronized" verwendet.


	public class SingletonSynchronized
	{
		private static SingletonSynchronized einzigeInstanz;
	 
		private SingletonSynchronized()
		{
		}
	 
		public static synchronized SingletonSynchronized getInstanz()
		{
			if (einzigeInstanz == null)
			{
				einzigeInstanz = new SingletonSynchronized();
			}
			return einzigeInstanz;
		}

		public void Function()
		{
			Console.WriteLine("Ich bin eine synchronisierte Instanz");
		}
	}

Dieses Schlüsselwort gibt es aber in C# nicht, oder?
Gibt es ein anderes Wort dafür?

Danke im Voraus

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo peterchen72.

in C# gibt es ==> lock, wird aber etwas anders verwendet.

herbivore

S
8.746 Beiträge seit 2005
vor 17 Jahren

Die direkte Entsprechung in .NET wäre es, die Methode mittels

[MethodImpl(MethodImplOptions.Synchronized)]

auszuzeichnen.

Das entspricht im Prinzip eines lock(this) zu Beginn der Methode. Wie aber auch bei lock(this) ist generell Vorsicht geboten. Führt schnell zu Deadlocks, wenn man nicht aufpasst.

4.207 Beiträge seit 2003
vor 17 Jahren

Verwende einen statischen Konstruktor.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

D
10 Beiträge seit 2007
vor 16 Jahren

Ich habe eine Frage zu [MethodImpl(MethodImplOptions.Synchronized)] und dachte ich schreibe das mal hierhinein, weil es grob zum Thema passt. In der Dokumentation habe ich leider nichts dazu gefunden.

Ich habe 2 Methoden Log( Object o ) und Log( string s ) und beide haben den oben genannten Zusat.
Könnte es passieren, dass Log( Object o ) und Log( string s ) gleichzeitig ausgeführt werden? Denn bei mir verwenden beide den selben Streamwriter, was natürlich zu Exceptions führen kann. Sollte ich das lieber per lock lösen?

Edit:

Sorry, habs gerade doch gefunden:

[MethodImpl(MethodImplOptions.Synchronized)]  

Dieses Attribut sperrt das Objekt StringResourcesDALC für die Dauer des Methodenaufrufs und blockiert dabei andere Aufrufe. Wenn Ressourcen zwischengespeichert sind, wird die Datenzugriffskomponente nicht aufgerufen, was die Leistung steigert.

Wenn ich das richtig lese, werden dann gar keine Methoden der Klasse aufgerufen während die so markierte Methode läuft, korrekt?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Der Dude,

so wie ich das sehe ist für Instanzmethoden


[MethodImpl(MethodImplOptions.Synchronized)]
public void M ()
{
   //...
}

und


public void M2 ()
{
   lock (this) {
      //...
   }
}

gleichbedeutend. Damit werden nur Methoden gegenseitig gesperrt, die mit [MethodImpl(MethodImplOptions.Synchronized)] ausgezeichnet sind und natürlich nicht alle Methoden eines Objekts. Und außerdem gilt die Sperre nur objektweise, d.h. eine mit [MethodImpl(MethodImplOptions.Synchronized)] ausgezeichnete Methode kann also durchaus mehrfach gleichzeitig aufgerufen werden, halt für unterschiedliche Objekte.

herbivore

383 Beiträge seit 2006
vor 16 Jahren

....
das entspricht im Prinzip eines lock(this) zu Beginn der Methode. Wie aber auch bei lock(this) ist generell Vorsicht geboten. Führt schnell zu Deadlocks, wenn man nicht aufpasst.

Hallo svenson

Nur zum Verständnis.. wenn man anstelle des 'lock(this)' ein privates Feld als Sperrobjekt nimmt sollte man nicht so schnell reinlaufen, oder? - Vorausgesetzt der Code innerhalb des lock {} führt nicht ins Nirvana.


internal sealed class MyClass {

private Object m_lock = new Object();

public void DoSomething() {
    lock(m_lock) {
    // auf gemeinsame Ressourcen zugreifen...
    }    
}

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo wakestar,

ja, das ist der Vorschlag von Microsoft.

herbivore

383 Beiträge seit 2006
vor 16 Jahren

Hallo herbivore

... und trotzdem ist es nicht 100% wasserdicht.

Hatte vorhin eine SynchronizationLockException (nachdem die Anwendung den ganzen Tag problemlos funktionierte).

wakestar

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo wakestar,

Eine SynchronizationLockException wird ausgelöst, wenn in einem nicht synchronisierten Codeblock eine der folgenden Methoden der Monitor-Klasse aufgerufen wird: Exit, Pulse, PulseAll oder Wait.

das hat aber nichts damit zu tun, dass es falsch wäre, ein lokales Sperrobjekt zu verwenden.

herbivore

383 Beiträge seit 2006
vor 16 Jahren

Hallo herbivore

...nein, falsch ist es sicher nicht. - Das lokale Sperrobjekt lass ich so.

Pulse, PulseAll oder Wait verwende ich nicht... aber Monitor.Exit wird ja ausgeführt weil ich das lock schlüsselwort verwende.
Folgende Methode wird im sekundentakt von einem Timer aufgerufen. ReadFile prüft zuerst ob eine gewisse Datei exisitiert, wenn ja.. wird diese verabeitet und gelöscht.


private void OnTimer(object state)
{
    lock (this.m_lock)
    {
        if (this.ReadFile(pathFilename))
        {
            this.DeleteFile(pathFilename);
        }
    }
}

3.971 Beiträge seit 2006
vor 16 Jahren

ja, das ist der Vorschlag von Microsoft.

So wie ich das verstanden habe ist


public class MyClass
{
  public void Func()
  {
    lock(this)
    {
      ...
    }
  }
}

in so fern "falsch", weil die Instanz in der die Funktion drin ist auch von außerhalb gelockt werden kann von einem anderen Thread außerhalb


MyClass inst = new MyClass();

lock(inst)
{
   ...
}

das zu einem Deadlock führen könnte

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo kleines_eichhoernchen,

genau das ist der Grund, den Microsoft angibt. Wobei ich den Rat für etwas übervorsichtig halte. Nehmen wir beispielsweise eine Collection coll, die intern lock (this) verwendet. Wenn nun ein Benutzer von außerhalb lock (coll) verwendet, um z.B. ein for-Schleife über die Collection zu sperren, sperrt die Collection intern mit demselben Objekt, wie der Benutzer der Collection für die Schleife. Einen Konflikt gibt es deswegen noch lange nicht. Es ist eher im Gegenteil genau das was man will, dass eben andere Operationen auf der Collection von anderen Threads nicht ausgeführt werden können, solange die Schleife läuft.

Ich kann mir erstmal keinen (vernünftigen) Fall vorstellen, wo es zu einem Deadlock führen würde, wenn man lock (this) verwenden würde.

herbivore

Suchhilfe: 1000 Worte, lock, this

4.207 Beiträge seit 2003
vor 16 Jahren

Objekt A macht intern ein lock(this) und ein lock(B), Objekt B macht intern ein lock(this) und ein lock(A) ... schon hast Du den Deadlock.

Daher sollte man NIE auf this locken, sondern IMMER auf ein explizites lock-Objekt.

Noch schlimmer ist das lock auf einen Typ, wie beispielsweise lock(Int32) ... hier wird alles innerhalb der AppDomain lahmgelegt, das diesen lock verwendet ... also auch hier immer schön auf ein statisches Objekt locken.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Golo,

wie ein Deadlock entsteht, ist mir schon klar. Mehr hast du ja im Prinzip nicht beschrieben. 🙂 Was ich mich nicht vorstellen kann, ist ein vernünftiges, also auch relevantes Beispiel, bei dem das zum Tragen kommt.

Andersherum kann ich nämlich auch ein Beispiel konstruieren, bei dem es bei der reinen Verwendung von internen Sperrobjekten zu einem Deadlock kommt.

herbivore

4.207 Beiträge seit 2003
vor 16 Jahren

Darauf wäre ich mal gespannt 😉

Sofern man sich innerhalb einer Klasse daran hält, alle Sperrobjekte immer in der gleichen Reihenfolge zu locken, kann innerhalb einer Klasse kein Deadlock vorkommen.

Oder habe ich da etwas übersehen?

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Golo,

richtig, wenn man eine feste Reihenfolge von Sperrobjekten einhält, kann es nie zu einem Deadlock kommen, egal ob die Sperrobjekte öffentlich sind oder nicht.

Was das Beispiel angeht, braucht mal also mindestens zwei Objekte (sagen wir A und B) und damit zwei interne Sperrobjekte. Jetzt muss man nur dafür sorgen, das ein Thread eine gesicherte/gesperrte Operation auf A auslöst, die innerhalb der Sperre einen Event auslöst. In dem EventHandler stößt man eine gesperrte Operation von B an. In einem anderen Thread macht man es genau umgekehrt und schon kann hat man den Deadlock, obwohl keiner der Threads direkt auf die Sperrobjekte zugreifen kann. Wie gesagt konstruiert, aber genau das sollte es ja auch sein.

herbivore

1.274 Beiträge seit 2005
vor 16 Jahren

Hallo zusammen,

es geht auch ganz ohne Lock (stimmt zwar nicht ganz, das Framework macht das für uns).

Ich war auch ziemlich erstaunt aber in einem MS Webcast hab ich diesen Ansatz gesehen.
Aufbau war ein einfacher Zähler, der sich in einer Klasse befindet, aber ein Code sagt mehr als tausend Worte.


sealed class SingletonCounter
{
   public int Counter = 0;

   private SingeltonCounter() //keine Instanzierung erlauben
   {
   }

   public static readonly SingletonCounter Instance = new SingletonCounter();
}

Dann Gute Nacht noch, vielleicht findet jemand daran eine Hacken
LastGentleman

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein