Autor Beitrag
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Do 29.01.15 20:41 
Hi,

ich möchte eine Datenbank-Schnitstelle von beliebig vielen Threads zugreifbar machen. Da ich aber davon ausgehen muss, dass verwendete Technologien das nicht unterstützen, wollte ich eine Art Pipeline nutzen, die Aufgaben für die Daten-Schnitstelle sammelt und wie ein Stack nach dem FiFo-Prinzip abarbeitet. Dieses Abarbeiten findet in einem eigenen ständig gleich bleibenden Thread start, in dem soll die einzige konkrete Datenbank-Kommunikation statt finden.

Nun möchte ich aber auch, dass ich auf die Beendigung eines Auftrages warten kann, wenn ich denn möchte, also das beim Aufruf der Methode, die einen neuen Auftrag auf den Stapel legt, ein Task-Objekt zurück gegeben wird. Außerdem möchte ich auf Rückgabewerte warten können.

Die tatsächliche Push-Methode sieht dann so aus:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
public async Task<TResult> Push<TResult>(Func<TResult> function)
{
    var job = new Job(function);
    _jobStack.Add(job);

    await Task.Factory.StartNew<TResult>(() =>
    {
        while (job.IsWaiting || job.IsRunning)
            Task.Delay(100);

        return job.ResultValue;
    });
}


Das ist auch gleich meine Idee, wie ich das selber implementieren würde.
Im Hintergrund läuft dann ein Thread, der permanent aus _jobStack Aufgaben nimmt und wenn keine da sind, wartet, bis welche kommen.


Meine Frage ist Folgende:
Gibt es bereits etwas in dieser Art oder hat jemand Vorschläge, wie ich das Vorhaben anders lösen kann?


Mein Ziel ist wie bereits gesagt eine Stack ähnliche Klasse, der ich ein Haufen Functionen übergeben kann und die dann in einem eigenen permanent Thread nach und nach abgearbeitet werden.
Das Ganze sieht dann so aus, als würde das in einem eigenen Task ablaufen, der Task macht aber im Endeffekt nichts Anderes, als warten, bis die Funktion beendet ist und gibt dann das Ergebnis zurück.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.01.15 21:16 
Zitat:
ich möchte eine Datenbank-Schnitstelle von beliebig vielen Threads zugreifbar machen.

Welche DB Zugriffstechnik sollte das nicht schon drauf haben?
Zitat:
wollte ich eine Art Pipeline nutzen, die Aufgaben für die Daten-Schnitstelle sammelt und wie ein Stack nach dem FiFo-Prinzip abarbeitet.

Wenn du alles serialisierst wo sollte es dann noch Probleme geben? Außer stinkige User die immer nur nacheinander an ihre Daten kommen? Ich vermute mal ich missverstehe da was bzw. sehe den Sinn der Serialisierung nicht (reden wir hier von einer SingleUser, SingleThread Datenbank)?
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Do 29.01.15 21:50 
Es geht darum, dass die konkrete Nutzung der Datenbank unbekannt ist.
Das kann ganz einfach SQL sein, oder das EntityFramework, was nicht multithreadingfähig ist, außerdem ist jede relationale Datenbank von Natur aus nicht multithreadingfähig, oder?
Ich kann auch keine serverseitige Anwendung bereit stellen, die den Zugriff auf die Datenbank verwaltet bzw. synchronisiert, da die User dann nicht mehr eigenständig fest legen können, wie die Datenbank konkret aus sieht oder angesprochen wird.

Das Problem dabei ist, dass die Datenbank auf einem Server liegt und der Zugriff kann auch mal von einer großen Zahl User statt finden, weshalb ich mir etwas einfallen lassen musste.
Da die Daten während der Bearbeitung beliebig oft gespeichert werden können, biete ich eine Möglichkeit, die Häufigkeit der Speicher-Zyklen genau fest zu legen, was aber auch mal sehr häufig statt finden kann. Daher muss das in einem eigenen Thread ablaufen.

Um das einfacher zu gestalten, war mein Vorhaben eine Art abgeschotteter Bereich, wo der Datenbank-Kontext erstellt und angesprochen wird. So bleibt dieser Konzext immer in ein und dem selben Thread.
Im Prinzip also etwas wie ein Dispatcher, der seine Aufgaben aus einem Stack nimmt und permanent ausführt, bzw. wartet, bis neue Aufgaben da sinf.


Zitat:
Wenn du alles serialisierst wo sollte es dann noch Probleme geben? Außer stinkige User die immer nur nacheinander an ihre Daten kommen?


Was meinst du mit Serialisierung, wie soll das helfen?
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.01.15 22:44 
Zitat:
Das kann ganz einfach SQL sein, oder das EntityFramework, was nicht multithreadingfähig ist, außerdem ist jede relationale Datenbank von Natur aus nicht multithreadingfähig, oder?


Ein solche Datenbank würde ich für fast unusable erklären. Natürlich ist der Zugriff auf Datenbanken Multithreading fähig. Aber wir denken wahrscheinlich an 2 verschiedene Dinge. Ich rede von der Datenbank und du sprachst von der Datenbank, du scheinst aber nicht die Datenbank zu meinen sondern die lokale offline Datenhaltung in irgendeinem Datencontainer (ADO Dataset, Recordset, EF DBSet, DBContext etc.). Die Datencontainer sind eher nicht Multithreading fähig. Ich glaube auch nicht das das irgendwo implementiert ist. Eine generische Lösung müsste soviel berücksichtigen das das meistens zu teuer ist. Was spricht bei deiner Anwendung mit EF dagegen jedem Thread seinen eigenen Context zu geben?

Zitat:
Was meinst du mit Serialisierung, wie soll das helfen?


Serialisierung = nacheinander ausführen lassen (seriell) im Gegensatz zur Parallelisierung. Hier Multithreadingfähigkeit herstellen in dem man verhindert das die Threads tatsächlich gleichzeitig laufen sondern schön nacheinander. Alternativ können wir auch Sequentialisierung sagen. Denn Begriff mag ich aber persönlich nicht. Und es sollte nicht helfen ich empfand es als das komplette Gegenteil als du von einer Warteschlange sprachst. Warum sollen nicht alle gleichzeitig auf die Datenbank (nicht Datencontainer) zugreifen?
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Do 29.01.15 23:45 
Stimmt, du hast Recht. Ich sprach von der Implementierung eines Datencontainers :D


Ich glaube, dass noch nicht so ganz klar ist, was ich vor habe.
Es geht nicht darum, mehrere Threads gleichzeitig (bzw. seriell) auf die Datenbank zugreifen zu lassen, es wird wahrscheinlich immer nur Einer sein. Ziel ist es dagegen, den Zugriff auf die Datenbank vollständig in einen eigenen Thread zu verlagern, damit der Thread, von dem das Speichern angestoßen wurde, nicht warten muss.

Die beiden Threads kommen nur dann in Berührung, wenn der eine Thread dem Kontext-Thread eine neue Aufgabe gibt.
Der Grund ist der, dass unter bestimmten Bedingungen jeder Schreib-Zugriff sofort gespeichert wird. Zusätzlich möchte ich dann noch einbauen, dass wartende Aufgaben geprüft und zusammen gelegt werden können um die Zahl der Speicher-Zugriffe zu reduzieren.



PS:
Jetzt habe ich beim Schreiben so viel über das Thema nach gedacht und einen anderen Weg:
Mein Ziel war ja, zu verhindern, dass sehr häufiges Speichern nicht zu stark verlangsamt. Eine Möglichkeit wäre, dass erst nach einer festen Zeit, in der keine Änderung getätigt wurde, gespeichert wird. Entweder der Zeitraum ist fest definiert, oder er wird durch Begin- und End-Methoden beim Aufruf vorgegeben.
Das letztendliche Speichern kann ich theoretisch dann auch in einen eigenen Thread verlagern, aber soweit ich mich erinnere, macht das async-Feature von .NET das etwas anders, sogar ohne Threads. Ein Task teilt die Aufgabe in viele kleine Teile auf, die dann im Aufrufer-Thread ausgeführt werden, oder?
Wenn ja, dann löst das mal eben mein Problem :D


Allerdings würde es mich trotzdem interessieren, ob mein altes Vorhaben auf diese Art möglich ist ^^