Laden...

Methoden für bestimmte Zeit blockieren

Erstellt von MaderaFunk vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.201 Views
M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren
Methoden für bestimmte Zeit blockieren

Hallo,
ich habe schon vieles ausprobiert und komme einfach nicht weiter, ich hoffe ihr könnt mir helfen.

Es handelt sich um eine WPF-Anwendung in Visual Studio.

Ich rufe momentan andauernd über einen MessageHandler mehrere Methoden auf, hier Methode A und B. Nun möchte ich gerne, dass während Methode A aufgerufen wird, die Methode B für ein bestimmtes Zeitintervall nicht aufgerufen wird, bzw. keine Funktion hat.

Ich dachte mir, ich löse das über eine if-Bedingung, die sagt, dass Methode B nur aufgerufen werden darf, wenn ein bool-Wert false ist.



static bool aufrufVerboten = false;

private void Methode A()
{
    
}

private void Methode B()
{
     if(aufrufVerboten == false)
     ...
}

Wird nun eine Methode A aufgerufen, möchte ich gerne "aufrufVerboten" für 5 Sekunden auf TRUE setzen. Wie kann ich das erreichen, dass währenddessen trotzdem Methode A normal weiterläuft?

Ich habe es mit "async" und "await" ausprobiert, aber es funktioniert bei mir nicht. Die Methode B wird zwar blockiert, aber ebenso Methode A. So sieht mein Code momentan aus:


static bool aufrufVerboten = false;

private void Methode A()
         {
              DelayAuslösen();
              ... // Der Code soll hier ständig weiterlaufen
         }

// Methode B soll, falls vorher Methode A aufgerufen wurde, für 5 Sekunden keine //Funktion haben
private void Methode B()
        {
            if(aufrufVerboten == false)
             ...
        }

static async Task<bool> DelayAndContinue()
        {          
            await Task.Delay(5000);    
            return false;
        }
        
static async void DelayAuslösen()
        {
            if (aufrufVerboten == false)
            {
                aufrufVerboten = true;
                aufrufVerboten = await DelayThenDoSomeWork();
            }
        }

DispatcherTimer und System.Timers habe ich ebenfalls getestet, überall wird die Anwendung blockiert und Methode A startet immer erst nach 5 Sekunden.
Wie könnte ich das Problem lösen?
Danke schonmal.

16.842 Beiträge seit 2008
vor 9 Jahren

Du musst in Methode A eine Thread-sicheres Feld (denk zB auch an volatile) auf einen bestimmten Status setzen, und diesen nach 5 Sekunden zurück setzen. Das funktioniert prinzipiell mit Timern problemlos.
Was Du hier falsch machst ist viele eher, dass Du async/await eher nicht verstanden hast und daher falsch anwendest.

In Methode B musst Du den Zustand abfragen, und entweder verhindern oder in der Methode warten, bis die Ausführung erlaubt ist (zB mit einem Monitor).
In [Artikel] Multi-Threaded Programmierung ist der Monitor erklärt.

Was ist der Sinn des ganzen?

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo MaderaFunk,

im Gunde eine Variante von [FAQ] Event nur bei Benutzeraktion auslösen, nicht bei programmtechnischer Änderung, nur dass du die Variable, wie es Abt gesagt hat, durch einen Timer erst nach 5 Sekunden zurücksetzt.

Allerdings bei Multi-Threading könnte es dann noch das Problem geben, dass A aufgerufen wird, kurz nachdem B aufgerufen wurde und mit der Verarbeitung noch gar nicht fertig ist. In dem Falle und auch nur, wenn das ein Problem darstellt, könnte man die Rümpfe beider Methoden durch lock schützen.

herbivore

M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren

Vielen Dank schonmal, ich werde das morgen ausprobieren.

Der Sinn des Ganzen ist, dass ich eine Gestenerkennung habe, die ständig die Position der Hände und Art der Geste abfragt. Habe ich nun 2 Hände mit einer Geste im Sichtfeld, so möchte ich eine andere Funktion steuern als wenn ich nur eine Hand habe. Das Problem ist, dass manchmal bei der Zweihandfunktion nur für den Bruchteil einer Sekunde eine Hand nicht erkannt wird und dann die Einhandfunktion ausgeführt wird, obwohl eigentlich zwei Hände da sind. Dies wollte ich unterbinden, in dem ich die Einhandfunktion für ein paar Millisekunden sperre (die 5 Sekunden in dem Code waren nur ein Beispiel).

M
334 Beiträge seit 2007
vor 9 Jahren

Wie wärs, wenn du einfach die Gestenerkennung deaktivierst, während die Zweihandfunktion ausgeführt wird?

M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren

Wie wärs, wenn du einfach die Gestenerkennung deaktivierst, während die Zweihandfunktion ausgeführt wird?

Das ist so nicht möglich, da die Zweihandfunktion auch während der Ausführung von der Gestenerkennung abhängt, ich also immer den aktuellen Status wissen muss.

Die Methode wird nicht nur einmalig aufgerufen, sondern andauernd und bekommt jedes mal neue Infos über den Status der Hände.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo MaderaFunk,

ich gehe mal davon aus, dass die Gestenerkennung im GUI-Thread, also single-threaded erfolgt. In diesem Fall reicht es aus, wenn du dir in der Methode für zwei Hände den jeweils aktuellen Zeitpunkt merkst (z.B. Environment.TickCount) und die Methoden für eine Hand sofort wieder verlässt, wenn dieser Zeitpunkt weniger als die 5 Sekunden (bzw. die entsprechende Anzahl von Ticks) in der Vergangenheit liegt.

herbivore

M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren

Die Idee gefällt mir, aber leider läuft die Gestenerkennung tatsächlich in einem anderen Thread. Ich habe es ausprobiert und es funktioniert daher leider nicht.
Momentan übergebe ich die Infos der Gestenerkennung mittels Dispatcher.Invoke an die GUI.

M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren


public volatile bool aufrufVerboten = false;
private System.Threading.Timer timer;

private void Methode A()
         {
              aufrufVerboten = true;
              timer = new System.Threading.Timer(TimerElapsed, null, 5000, 0);
              
              ... // Der Code soll hier ständig weiterlaufen
         }

private void Methode B()
        {
            if(aufrufVerboten == false)
            //wenn ich diese false Bedingung setze, wird auch der Aufruf von Methode A verzögert. Lasse ich sie weg, läuft Methode A ohne Probleme. 
             ...
        }

public void TimerElapsed(object state)
        {
            gesteAktiv = false;
            timer.Dispose();
        }
        

Ich habe mir eure Vorschläge und Links durchgelesen und einiges ausprobiert, aber konnte das Problem leider immer noch nicht lösen.
Mit dieser Lösung mit dem Timer habe ich z.B. das gleiche Problem. Wenn ich in Methode B die if-Bedingung weglasse, laufen sowohl A als auch B ohne Probleme. Wenn Methode B jedoch die if-Bedingung enthält, dann wird auch Methode A verzögert, was nicht sein sollte.

S
93 Beiträge seit 2008
vor 9 Jahren

ich denke, da müßte man etwas mehr Code sehen!!!

M
MaderaFunk Themenstarter:in
6 Beiträge seit 2014
vor 9 Jahren

ich denke, da müßte man etwas mehr Code sehen!!!

Ok, ich habe es mal versucht zusammenzufassen:


namespace Programm
{
    public partial class MainWindow : Window
    {
        GestenInit gesten;     

        public MainWindow()
        {
            InitializeComponent();

            //Gestensteuerung starten
            gesten = new GestenInit(this);
            gesten.Init();
        }
    }


    class GestenInit
    {
        MainWindow mWindow;
        HandTrackingClient client; 

        public GestenInit(MainWindow mW)
        {
            mWindow = mW;
        }

        public void Init()
        {
            client = new HandTrackingClient(); //dies läuft in einem separatem Thread (Zugriff auf dll)
            Gestensteuerung gestensteuerung = new Gestensteuerung(client, mWindow);
        }

    }


    class Gestensteuerung
    {
        readonly HandTrackingClient client;
        MainWindow mWindow;
        public volatile bool gesteAktiv = false;
        private Timer timer;

        public Gestensteuerung(HandTrackingClient client_in, MainWindow mW)
        {
            mWindow = mW;
            client = client_in;

            client.MessageCallback += new MessageHandler(HandleEvent);
            client.Connect();
        }

        public void HandleEvent(HandTrackingMessage message) //message enthält alle Infos über die aktuelle Geste
        {
            Methode A();
            Methode B();
        }

        private bool WriteToMainWindow(int wertMethodeA, int wertMethodeB)
        {

            if (!mWindow.Dispatcher.CheckAccess())
            {
                return (bool)mWindow.Dispatcher.Invoke((Func<int, int, bool>)WriteToMainWindow, wertMethodeA, wertMethodeB);
            }

            mWindow.SliderMethodeA.Value = wertMethodeA;
            mWindow.SliderMethodeB.Value = wertMethodeB;

            return false;
        }

        private void Methode A(HandTrackingMessage message)
         {
            //Das Problem hier ist, dass sobald nur für den Bruchteil einer Sekunde nur eine der beiden Hände nicht erkannt wird, sofort Methode B 
            //aufgerufen wird.
            if (linkeHandErkannt == true && rechteHandErkannt == true)
            {
              aufrufVerboten = true;
              timer = new System.Threading.Timer(TimerElapsed, null, 5000, 0);

              wertMethodeA++;
            }
         }

        private void Methode B(HandTrackingMessage message)
        {
            if ((linkeHandErkannt == true ^ rechteHandErkannt == true) && aufrufVerboten == false)
            {
             wertMethodeB++;
            }

        }

        public void TimerElapsed(object state)
        {
            gesteAktiv = false;
            timer.Dispose();
        }
    }
}

P
1.090 Beiträge seit 2011
vor 9 Jahren

Setze bei A mit DateTime.Now einen Zeitstempel und prüfe bei B ob DateTime.Now - Zeitstempel größer ist als deine Wartezeit.

MFG
Björn

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern