Laden...

Threadsicher auf eine Abfrage zugreifen

Erstellt von padde77 vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.179 Views
P
padde77 Themenstarter:in
50 Beiträge seit 2011
vor 6 Jahren
Threadsicher auf eine Abfrage zugreifen

Hi,

ich habe eine Methode A, welche mir einen Ergebnisdatensatz zurück liefert, und zwar anhand eines Parameters. Diese Methode wird jedoch von verschiedenen Threads aufgerufen,
Thread 1 -> Methode A(1)
Thread 2 -> Methode A(2)
...

Mein Problem ist nur, dass in der Methode eine Abfrage auf eine DLL statt findet, und diese kommt nur mit einer gleichzeitigen Verbindung klar. Also kann müsste die Abfrage des 2., 3. usw. erst auf das Ergebnis des ersten Aufrufes warten.
Wie kann ich dies am besten bewerkstelligen?

Ein kleines Snippet wäre auch nett.

Danke Patrick

T
2.219 Beiträge seit 2008
vor 6 Jahren

Stichwort ist hier Thread Locking.
Kannst du mit dem Keyword lock und einem entsprechenden Locking Objekt erledigen.
Musst dann natürlich den Aufruf der DLL Methode am besten kapseln und dann über deine Methode mit dem Lock aufrufen.

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.

2.078 Beiträge seit 2012
vor 6 Jahren

Den Aufruf selber kannst Du zwar threadsafe gestalten, aber ich würde es nicht machen.
Besser ist, wenn die Methode von sich aus threadsafe ist und sich darum kümmert, das nur ein Thread gleichzeitig auf die DLL zugreifen kann.
So kannst Du beim Aufruf nix falsch machen.

Und wie das geht: lock
Das kann genau das, was Du haben willst.

P
padde77 Themenstarter:in
50 Beiträge seit 2011
vor 6 Jahren

OK, ich werde mir das einmal ansehen.
Lock heisst dann, dass sich Thread 4, 8 und 9 hinten anstellen müssen, bis z.B. Thread 2 seine Rückgabe hat und die Methode sagt: "der nächste bitte". Korrekt?

Danke und Gruß
Patrick

2.078 Beiträge seit 2012
vor 6 Jahren

So ungefähr, ja.

Das übergebene Objekt dient dabei als eine Art Schlüssel, Typ ist dabei egal.
Wichtig ist nur, dass die Instanz immer die Gleiche ist.

P
padde77 Themenstarter:in
50 Beiträge seit 2011
vor 6 Jahren

...noch eine kurze Frage:

kann ich innerhalb eines locks eine andere Method mit Rückgabe aufrufen und wartet das lock trotzdem?
Kann leider nicht testen, da ich heute keine Testumgebung hier habe, daher die Frage, sorry.

also so:

lock(obj)
{
    var test = NeueMethode(parameter);
}

Vielen Dank

T
2.219 Beiträge seit 2008
vor 6 Jahren

@padde77
Kannst du.
Du kannst auch eine weitere Methode, die ein Lock hat aufrufen.
Dann muss aber ggf. der Aufruf von dir auch auf andere Threads, die gerade diese Methode aufrufen, warten müssen.

Kannst du aber auch mal etwas rumtesten, dann kannst du das Verhalten nachvollziehen.
Gerade um auf geteilte Resourcen zuzugreifen, solltest du dich mit dem Thema gut beschäftigen.

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.

16.806 Beiträge seit 2008
vor 6 Jahren

Bevor man direkt irgendwas mit Threads wurschtelt, sollte man mal schauen, ob es nicht einen Pattern gibt, mit dem man das Vorhaben strukturiert umsetzen kann.

Hier scheint es ja auch irgendein Logik- oder Datenteil zu betreffen, für das es in den meisten Fällen passende Pattern gibt.

T
2.219 Beiträge seit 2008
vor 6 Jahren

@Abt
Sollte eigentlich logisch sein.
Aber gerade da der TE scheinbar noch ein Defizit bei dem Thema Threads hat, sollte er mit einem Testprogramm mal rumspielen und sich durch die Doku lesen um das entsprechende Wissen aufzubauen.

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.

16.806 Beiträge seit 2008
vor 6 Jahren

Ja aber das hier hört sich nicht nach einem Testprogramm an.

Und Pattern sind auch für Anfänger geeignet.

2.078 Beiträge seit 2012
vor 6 Jahren

@padde77:

Du solltest dir auch mal das Wort Deadlock anschauen, einer der Fehler, den wahrscheinlich jeder Entwickler beim Thema Multithreading fürchtet 😄
Wie der Name sich zusammen setzt, kannst Du dir vielleicht selber erklären 😄

Code-Beispiel:

Console.WriteLine("before lock 1");

lock (syncRoot)
{
    Console.WriteLine("in lock 1");

    Console.WriteLine("create thread");

    var thread = new Thread(() =>
    {
        Console.WriteLine("before lock 2");

        lock (syncRoot)
            Console.WriteLine("in lock 2");

        Console.WriteLine("thread ready");
    });

    Console.WriteLine("start thread");
    thread.Start();

    Console.WriteLine("wait for thread");
    thread.Join();
}

Console.WriteLine("ready");

Führ das mal aus, Du wirst fest stellen, dass der Code nie zu einem Ende kommt.

Der Grund ist:
Der Main-Thread läuft in das lock rein.
Er startet einen Thread, der ebenfalls ein lock auf das selbe Objekt enthält.
Anschließend wartet er auf diesen Thread.

Das Problem:
Der Main-Thread blockiert das Objekt für sich.
Der zweite Thread wartet nun darauf, dass das Objekt wieder frei wird.
Der Main-Thread wartet auf den zweiten Thread und gibt danach dann das Objekt wieder frei.
Blöd ist nur, dass das Objekt nie frei wird, bevor der Thread nicht beendet ist. Und der Thread wird nie beendet bevor er zuende ist, weil der Main-Thread das Objekt vorher nicht frei gibt.

Du erkennst das Problem? ^^

P
padde77 Themenstarter:in
50 Beiträge seit 2011
vor 6 Jahren

Hi, ja, danke für eure Antworten.
Das mit dem Deadlock ist klar. und das mit den Patterns schaue ich mir mal an.

Danke für eure Informationen

Grüße
Patrick

A
2 Beiträge seit 2004
vor 6 Jahren

Das mit dem lock() funktioniert übrigens bei Calls aus async/await heraus nicht mehr.

B
66 Beiträge seit 2013
vor 6 Jahren

Singleton bilden:
Singleton (Entwurfsmuster)

using System;
using System.Reflection;
using System.Windows.Controls;

namespace XXXXXX
{
    public abstract class SingletonReflectionConstruct<T>
    {
        public static T Instance
        {
            get
            {
                return SingletonFactory.Instance;
            }
        }

        internal static class SingletonFactory
        {
            internal static T Instance;

            static SingletonFactory()
            {
                CreateInstance(typeof(T));
            }

            public static T CreateInstance(Type type)
            {
                ConstructorInfo[] ctorsPublic = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public);

                if (ctorsPublic.Length > 0)
                {
                    throw new Exception(string.Concat(type.FullName, " has one or more public constructors so the property cannot be enforced."));
                }

                ConstructorInfo nonPublicConstructor =
                    type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

                if (nonPublicConstructor == null)
                {
                    throw new Exception(string.Concat(type.FullName, " does not have a private/protected constructor so the property cannot be enforced."));
                }

                try
                {
                    return Instance = (T)nonPublicConstructor.Invoke(new object[0]);
                }
                catch (Exception e)
                {
                    throw new Exception(
                        string.Concat("The Singleton could not be constructed. Check if ", type.FullName, " has a default constructor."), e);
                }
            }
        }
    }

    public class BuildCanvas_Singleton : SingletonReflectionConstruct<BuildCanvas_Singleton>
    {
        private BuildCanvas_Singleton()
        {
            _canvas1 = new Canvas();
            _count++;

        }

        // Instance counter.
        private static int _count = 0;
        private static Canvas _canvas1;

        public static int Count { get { return _count; } }
        public static Canvas GetCanvas { get { return _canvas1; } }
    }
}

Die Implementierungen des zu testenen Code.


        BuildCanvas_Singleton singleton1 = BuildCanvas_Singleton.Instance;

        public Canvas canvas1 = BuildCanvas_Singleton.GetCanvas;
        public Canvas canvas2 = BuildCanvas_Singleton.GetCanvas;

evtuell mit null object:
Nullobjekt (Entwurfsmuster)

D
985 Beiträge seit 2014
vor 6 Jahren

Threadsicher := Singleton bilden

Kannst du das bitte einmal erläutern, warum das deiner Meinung nach so sein sollte?

Ich halte diese Aussage für groben Unfug, bin aber nicht lern-/beratungs-resistent

B
66 Beiträge seit 2013
vor 6 Jahren

Ok! -Gebe ich dir Recht. Zu pauschal.

Grüße

D
985 Beiträge seit 2014
vor 6 Jahren

Und wie hilft jetzt so ein Singleton dem TE?

Ich sehe da bislang keinen Zusammenhang.

B
66 Beiträge seit 2013
vor 6 Jahren

Was?
Du bildest ein Singleton durch die Fabrik mit Methode A.

 public class Methode_A_Singleton : SingletonReflectionConstruct<Methode_A_Singleton>
{
      // .... Hier Schreibst Du das Threadhandling der Clients rein, die alle aktiv sein müssen.
      //  ... evt. Shunts, Hubs oder Mocks (s.u.)
}

So ruft jeder einzelne Thread die selbe Instanz von Methode A auf.
Am Besten über ein Nullobject, damit wenn ein Thread beendet wird (Dispatcher), wir noch weiterhin eine gültige Referenz für das Singleton und die anderen Threads haben.

Was mir auch noch einfällt wäre Threadaufrufe zu mockeo. o.ä. (s. Shunt, Hubs und Mocks)

Was erwartest Du? Das ich bei der abstrakten Form Meter von Code schreibe?

16.806 Beiträge seit 2008
vor 6 Jahren

Genau so ein Konstrukt führt aber dazu, dass Race Conditions entstehen (können) und das Debugging unheimlich aufwändig und teuer wird, sollte mal was sein.

Singleton ist hier für mich die falsche Herangehensweise.
Da wäre eine entsprechendes Messaging in meinen Augen (zB Pipelining) leistungsfähiger, übersichtlicher und am Ende besser.

B
66 Beiträge seit 2013
vor 6 Jahren

Ist genauso theoretisch falsch, weil man nicht weiß, was die Dll macht, noch was er mit den Threadaufrufen bewirken will.

Racing constraints können nur entstehen wenn die Dll andere Dll aufruft über die du dann keine Kontrolle hast.

Ergo: Ohne Code des TE raten wir nur rum...

16.806 Beiträge seit 2008
vor 6 Jahren

Racing constraints können nur entstehen wenn die Dll andere Dll aufruft über die du dann keine Kontrolle hast.

Hää? Was haben denn Race Constraints mit DLLs am Hut? 🤔

B
66 Beiträge seit 2013
vor 6 Jahren

Was hat ein Zirkelbezug mit Dlls am Hut? 😉

Und Du weist auch nicht welche Abhängigkeiten noch zwischen den Client Threads bestehen. Deshalb raten wir hier nur rum.

16.806 Beiträge seit 2008
vor 6 Jahren

Sorry, dass ich das jetzt fragen muss (auch wegen anderen aktuellen Beiträgen von Dir), aber sind Deine Beiträge hier gerade wirklich ernst oder ist das ein Trollversuch?

PS: bitte gemäß [Hinweis] Wie poste ich richtig? keine Full Quotes mehr.