Hallo zusammen,
ich habe in meine Web-Projekt eine threadsichere Singleton implementiert.
private static object syncRoot = new Object();
public static RamboService GetInstance(string pageDbPathForFastData, string pageDbPathForSlowData)
{
if (instance == null)
{
lock (syncRoot)
{
logger.InfoFormat("Creating static RamboService in threaed {0}", Thread.CurrentThread.ManagedThreadId);
instance = new RamboService(pageDbPathForFastData, pageDbPathForSlowData);
}
}
return instance;
}
In der Entwicklungsumgebung (IIS-Express) wird diese auch genau einmal erzeugt, wie es sein soll.
Lade ich das ganze jedoch auf dem produktiven IIS hoch, wird die Singleton mehrmals von verschiedenen Threads erzeugt.
Wie kann ich das umgehen? bzw. Woran liegt es ?
Singleton haben in 99% der Fälle in Web Anwendungen nichts zu suchen.
Warum verwendest Du keine Dependency Injection, wie man es machen soll?
Webanwendungen erzeugen pro Request einen eigenen Thread. Nur weil Du einen Singleton hast, bist Du noch lange nicht Thread-sicher. Das sind zwei paar Stiefel.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Mh? Ich versteh die Frage nicht...
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Wo muss ich ansetzen. Ist für mich Neuland.
Brauch ich hierfür ein Framework oder ähnliches?
Einfach mal in die Doku schauen. Dafür ist sie da.
ASP.NET MVC 4 Dependency Injection
oder für ASP.NET Core
Introduction to Dependency Injection in ASP.NET Core
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Okay, ich habe das jetzt mal so weit nachprogrammiert.
Mein Problem ist, dass ich jetzt immer, wenn ich einen Typ anfordere eine neue Instanz dieses Typen bekomme. Das Problem mit der Abhängigkeit (welches ich ja eigentlich gar nicht hatte) ist damit gelöst, aber wie bekomme ich denn mit DI immer die gleich Instanz eines Typen ?
Oder muss ich jetzt innerhalb des Typs wieder ein Singleton implementieren ?
Wieso liest Du nicht einfach die Doku? Keine Lust? Arbeit auf uns abwälzen?
Damit wären alle Deine Fragen gelöst.
Mein Problem ist, dass ich jetzt immer, wenn ich einen Typ anfordere eine neue Instanz dieses Typen bekomme.
Ist prinzipiell auch das richtige Vorgehen in Web Anwendungen.
Warum denkst Du denn, dass Du einen Singleton oder immer die gleiche Instanz brauchst?
Singletons sind prinzipiell ein Hinweis auf ein Architekturfehler.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Wieso liest Du nicht einfach die Doku? Keine Lust? Arbeit auf uns abwälzen?
Habe ich, entschuldige bitte, wenn ich ein für mich neues Thema nicht direkt verstehe 😕
Mein Kernproblem ist, dass ich einen Cache implementieren will, der einmalig ist. Was zwei Stunden vorher während irgendeinem Web-Request in den Cache geladen wurde, soll zwei Stunden später auch noch vorhanden sein.
Ich wüste nicht, wie ich das anders lösen kann außer durch eine Objekt, was genau einmal instanziert wird und dann global verfügbar ist.
Du willst in 20 Minuten die Dokumentation vollständig gelesen (und verstanden haben)?
Im Leben nicht.
Reden wir von ASP.NET 4 oder ASP.NET Core? Was soll gecached werden?
Core hat einen Cache Service mit an Board.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ich benutze ASP.NET 4.
Mein Cache ist auch komplett fertig entwickelt. Es ist eine Klasse, die bestimmte Daten bei Bedarf aus Dateien lädt und diese nach komplexen Kriterien von 1 Stunde bis zu mehreren Tagen im Cache hält. Das funktioniert auch wunderbar, so lange alle die selbe Instanz verwenden.
@Christoph K.
Ist überflüssige Arbeit.
Nutz ASP .NET Bordmittel.
Zum Cachen kannst du seit .Net 2.0 den HTTP Cache verwenden.
Diesen kannst du auch so umsetzen, dass dein Objekt zeitlich begrenzt gespeichert wird.
Link:
ASP.NET Caching
Weiteres Link zu aktuellen Verfahren bei MVC musst du mal suchen.
Aber selbst dort dürfte es fertige Lösungen geben, die dein caching überflüssig machen.
Hier macht es keinen Sinn das Rad neu zu erfinden.
Nachtrag:
heir ein passender Link:
Extensible Output Caching with ASP.NET 4 (VS 2010 and .NET 4.0 Series)
T-Virus
Developer, Developer, Developer, Developer....
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
Vielen Dank, dass mit dem Cache klingt sehr interessant
Was mich immer noch verwundert:
Ich habe gerade mal nachgesehen und es gibt bei meinem IIS Projekt nur einen Worker-Process. Auch die Kontrolle im Code liefert immer die gleiche ProcessId.
Laut der Dokumentation teilen sich alle Threads eines Processes die gleichen statischen Variablen. Wie kann es demnach sein, dass sich meine Singletons zweimal initialisieren?
Der Worker Process hat damit null zutun. Bitte dazu die Grundlagen zum IIS anschauen.
Das ist ein ganz anderes Thema. Du vermischt wegen fehlenden Grundlagen leider allgemein sehr viel.
Laut der Dokumentation teilen sich alle Threads eines Processes die gleichen statischen Variablen.
Wo hast Du das gelesen? Oder auch nur überflogen? Diese Aussage ist nämlich falsch.
Ein Prozess kann zwar mehrere Threads haben, ein Thread ist immer an einen Prozess gekoppelt.
Statische Variablen sind aber nicht an einen Thread oder Prozess gekoppelt, sondern an eine AppDomain. Und ein Prozess kann mehrere AppDomains haben (was aber meistens nicht der Fall ist; nur wenn man es aktiv will, zB. Pluginsystem).
Das Thema AppDomain vergiss aber bitte wieder, weil ist zu tief an dieser Stelle.
Allgemein wird das Problem aber nicht an der Tatsache eines Singleton liegen, sondern weil Du irgendwo einen Fehler im Code hast, den wir hier nicht sehen.
Deine Singleton Implementierung an dieser Stelle hat sowieso Mangel: egal welche Parameter man übergibt, es würde immer die erste Erzeugung zurück geben.
So entwickelt man keine Singleton Factory.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Liegt wohl daran, dass dein Instanz-Erzeugungscode nicht threadsafe hinsichtlich dieses Merkmals (nur eine Instanz) ist.
... bzw die Abfrage, ob eine Instanz existiert nicht im lock { } liegt.
Nun ja, ich würde einen Check aussen und innen vornehmen um nicht jeden parallelen Zugriff in eine gezwungene Synchronisierung zu zwingen.
private static object syncRoot = new Object();
public static RamboService GetInstance(string pageDbPathForFastData, string pageDbPathForSlowData)
{
if (instance == null)
{
lock (syncRoot)
{
instance = instance ?? new RamboService(pageDbPathForFastData, pageDbPathForSlowData);
}
}
return instance;
}
Das bedeutet jetzt nicht, dass ich diese Art des Singletons gutheißen möchte. Gerade auch im Hinblick auf die übergebenen Argumente. Sehr gruselig.
Nun ja, ich würde einen Check aussen und innen vornehmen um nicht jeden parallelen Zugriff in eine gezwungene Synchronisierung zu zwingen.
private static object syncRoot = new Object(); public static RamboService GetInstance(string pageDbPathForFastData, string pageDbPathForSlowData) { if (instance == null) { lock (syncRoot) { instance = instance ?? new RamboService(pageDbPathForFastData, pageDbPathForSlowData); } } return instance; }
Das bedeutet jetzt nicht, dass ich diese Art des Singletons gutheißen möchte. Gerade auch im Hinblick auf die übergebenen Argumente. Sehr gruselig.
Was meinst du genau mit außen und innen ?
Bei dem Beispiel war der lock(syncRoot) an der falschen stelle. Ich habe ihn jetzt nach außen gebracht. Aber wozu brauche ich den inneren dann noch?
Das mit den Argumenten ist nicht schön, ich weiß - allerdings sind die Argumente immer die gleichen und hängen nur davon ab, in welchem Projekt ich die Klasse verwende.
Mit Außen und Innen bezog ich mich auf den lock
.
Hier mal zum besseren Verständnis ein leicht abgewandelter Code-Schnipsel:
public static RamboService GetInstance(...)
{
#region Optional
// Dieser Code ist für die zuverlässige Funktion nicht erforderlich
// allerdings verhindert er, dass JEDER Aufruf durch den LOCK muss
// Wenn es eine Instanz gibt
if ( instance != null )
// dann können wir diese auch gleich zurückgeben
return instance;
#endregion
lock(syncroot)
{
// Wenn wir den lock erhalten haben, müssen wir nochmals prüfen
// ob wir jetzt wirklich keine Instanz haben
if (instance == null )
// Erzeugen der Instanz
instance = new RamboService(...);
return instance;
}
}
Das mit den Argumenten ist nicht schön, ich weiß - allerdings sind die Argumente immer die gleichen und hängen nur davon ab, in welchem Projekt ich die Klasse verwende.
Dann lies aus einer Config statt Parameter zu verwenden.
So ist das strukturell einfach logisch falsch.
Dann kann man auch einfach Lazy<T> verwenden, wenn es überhaupt sein muss.
Ich sehe hier immer noch ganz klar Dependency Injection in der richtigen Rolle statt dieses gebastle.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code