Hallo zusammen, ich beschreibe erstmal die Randbedingungen. Eine Methode berechnet einen Modus an einer verbunden Peripherie. Diese Berechnung erfordert eine "Pause" die dynamisch ist, daher berechnet wird. Sprich die Methode fragt einige Dinge an, muss dann kurz pausieren (das kann man leider nicht Ändern, das ist ein MUSS), und fragt dann noch ein paar dinge ab. Anschließend hat die Methode alle nötigen Daten um den Modus zu berechnen.
Die Methode hängt an einem Service welcher im GUI Thread erzeugt wurde. Nun gibt es 2 Möglichkeiten den Modus anzufragen. Zum einen Synchron, daher direkt. Zum anderen asynchron, daher im Service selber wird die Methode in einem separaten Thread ausgeführt. Ist der Thread beendet, wird dem Caller der Asynchronen Methode via Callback bescheid gegeben, das dass Ergebnis des Modus nun vorliegt.
Wird die Async Methode ausgeführt, so gibt es noch die Möglichkeit den Berechnung abzubrechen. Dazu wird einfach eine boolsche Variable gesetzt, welche im Thread immer wieder abgefragt wird, und im falle diese ist true, wird via return die Methode die dem Thread zugeteilt ist verlassen und damit die Berechnung beendet.
Mir geht es jetzt um das Design des Interrupts. Klingt vielleicht einfach, jedoch gibt es da wie immer mehrere Ansätze. Mir fallen Spontan 3 Stück ein. Die GetModi Methode liefert den Modus. Diese kann entweder direkt gerufen werden, oder über einen separaten Thread. Daher muss die GetModi() Methode immer wieder das oben genannte bool abfragen (volatile aborted).
Modi GetModi()
{
....
if (aborted) return Modi.Aborted;
....
if (aborted) return Modi.Aborted;
....
//here we need the timer interrupt for example 4000 ms
//we could not ask for the aborted flag during the interrupt
Thread.Sleep(4000);
....
if (aborted) return Modi.Aborted;
....
}
Modi GetModi()
{
....
if (aborted) return Modi.Aborted;
....
if (aborted) return Modi.Aborted;
....
//here we need the timer interrupt for example 4000 ms
Timer t = new Timer(4000);
this.m_interruptReached = false;
t.Elapsed += this.Elapsed;
t.Start();
while (!this.m_interruptReached)
{
//polling...
if (aborted) return Modi.Aborted;
}
t.Stop();
....
if (aborted) return Modi.Aborted;
....
}
void Elapsed(object sender, EventArgs args)
{
this.m_interruptReached = true;
}
Modi GetModi()
{
....
if (aborted) return Modi.Aborted;
....
if (aborted) return Modi.Aborted;
....
//here we need the timer interrupt for example 4000 ms
//we could ask for the aborted flag between the time interrupt
for (int i = 0; i < 8; ++i)
{
Thread.Sleep(500);
if (aborted) return Modi.Aborted;
}
....
if (aborted) return Modi.Aborted;
....
}
Ich weis, Thread.Sleep ist nicht schön, jedoch hätte es den Vorteil, das während des Sleeps ein anderer Thread die CPU Power bekommt, das wäre im 2ten Beispiel nicht der Fall (Polling). Gibt es für solche UseCases ein Design Pattern, mir ist dafür leider keines bekannt. Was sagt Ihr dazu, wie würdet ihr das Designen mit den gegebenen Vorgaben?
Again what learned...
Hallo,
Die Methode hängt an einem Service welcher im GUI Thread erzeugt wurde.
Die UI sollte nie warten, denn sonst friert sie ein.
Eine Methode berechnet einen Modus an einer verbunden Peripherie. Diese Berechnung erfordert eine "Pause" die dynamisch ist, daher berechnet wird. Sprich die Methode fragt einige Dinge an, muss dann kurz pausieren (das kann man leider nicht Ändern, das ist ein MUSS), und fragt dann noch ein paar dinge ab.
Ich würde das über 2 Threads lösen die mit den Methoden der Monitor-Klasse synchronisiert/gepulst werden. Wie das gehts zeigt zB SyncQueue <T> - Eine praktische Job-Queue sehr schön.
Wie wird übrigens die Pause berechnet?
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
Wie wird übrigens die Pause berechnet?
Das ist von einigen Dingen der Peripherie abhängig, das kann/darf ich hier nicht genauer erklären. Wichtig ist, im normal falle sind die Interrupts/pausen im Millisekunden Bereich, 300-800ms. Von daher friert die GUI nicht gleich ein.
Die UI sollte nie warten, denn sonst friert sie ein.
Da hast du eigentlich recht, nur folgende Randbedingung sind einzuhalten. Die Methode integriert sich in ein Framework welches die Methode auch synchron aufruft, daher erwartest es auch das Ergebnis synchron. Dem entsprechend kann ich dort den Aufruf leider nicht beeinflussen.
Again what learned...
Die Methode integriert sich in ein Framework welches die Methode auch synchron aufruft, daher erwartest es auch das Ergebnis synchron. Dem entsprechend kann ich dort den Aufruf leider nicht beeinflussen.
Aber du kannst die Gui logisch von deinem Framework/BusinessClass trennen - Ist kein muss, nur sind die Anwender von heute sehr verwöhnt.
Alternative zu Thread.Sleep ist ein Timer.
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Hallo,
du musst ja nicht detailliert erklären wie die Pause berechnet wird. Das sollte eh separat erfolgen. Von daher frag ich anders: Wie bekommt die GetModi-Methode den Wert für die Pause mitgeteilt? Ohne das zu wissen kann auch kein vernüftiger Vorschlag gegeben werden.
Auch wenn es Vorgabe ist das synchron aufzurufen kann die Ausfürhung nebenläufig erfolgen. Das "Ergebnis-Event" muss dann halt mit dem UI-Thread synchronisiert werden.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Alternative zu Thread.Sleep ist ein Timer.
Die 2 Vorschläge habe oben schon aufgeführt. Nur welcher ist besser?
Wie bekommt die GetModi-Methode den Wert für die Pause mitgeteilt?
Die GetModi Methode berechnet die Pause anhand von Daten die es im GetModi von der Peripherie bekommt.
Auch wenn es Vorgabe ist das synchron aufzurufen kann die Ausfürhung nebenläufig erfolgen.
Ich denke das geht nicht, weil den Aufruf kann ich nicht beeinflussen. Daher derjenige der mich ruft erwartet das Ergebnis im Return Value der Methode. Daran kann man leider nix Ändern, sprich über ein Event synchronisieren ist leider nicht möglich.
Again what learned...
Hallo,
kannst du das vorgegeben Umfeld mal kurz skizzieren? Also was ist gegeben und kann / kann nicht geändert werden.
Wäre folgendes möglich?
Modi GetModi()
{
// fragt einige Dinge an
// Berechnung der Dauer der Pause
int pauseTime = 100;
Modi modus = null;
using (ManualResetEventSlim mre = new ManualResetEventSlim())
using (Timer timer = new Timer(
_ =>
{
// Berechnung des Modus
modus = BerechneModus();
mre.Set();
},
null,
pauseTime,
Timeout.Infinite))
{
mre.Wait();
}
return modus;
}
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo rollerfreak2,
wenn man in einem Vorgang eine bestimmte Zeitspanne warten will, den Vorgang aber auch während dieser Zeitspanne abbrechen können will, bietet es sich an, die Sache gedanklich umzudrehen, also vorrangig auf den Abbruch zu warten, aber das maximal eine bestimmte Zeitspanne lang, sprich z.B. einen AutoResetEvent mit TimeOut zu verwenden. Damit vermeidet man jede Art von Polling und trotzdem (oder gerade deshalb) wird ein Abbruch verzögerungsfrei behandelt. Also weder Timer noch Thread.Sleep sind hier optimal.
herbivore
Hallo,
welche .net-Version verwendest du?
Für .net 4.0 gibts mit Cancellation elegante Möglichkeiten um das von herbivore vorgeschlagene umzusetzen. Schau dir dann auch das PDF aus Patterns for Parallel Programming with the .NET Framework an - ich denke der Abschnitt (bzw. das Stichwort für die Suche im Dokumnet) "For the future" könnte passend sein.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
welche .net-Version verwendest du?
Wir sind leider auf 2.0 beschränkt! Das von herbivore vorgeschlagene klingt zwar gut, löst aber das Problem des Wartens nicht, oder ich hab es nicht richtig verstanden. Also die Methode GetModi soll zu jeder Zeit abbrechenbar sein! Desweiteren soll die Methode an einer bestimmten Stelle im Code eine bestimmte Zeit warten, ohne jedoch die Methode zu verlassen.
Das mit dem AutoResetEvent schau ich mir mal an. Wer raised das denn, und wie beende ich dann die Methode? Und was hat das mit der von mir erwähnten Pause zu gemeinsam?
Again what learned...
Hallo rollerfreak2,
Das von herbivore vorgeschlagene klingt zwar gut, löst aber das Problem des Wartens nicht, oder ich hab es nicht richtig verstanden. Also die Methode GetModi soll zu jeder Zeit abbrechenbar sein!
doch, genau das wird mit meinem Vorschlag erreicht.
Das mit dem AutoResetEvent schau ich mir mal an. Wer raised das denn, und wie beende ich dann die Methode?
Die Methode, die aufgerufen wird, um die Aktion abzubrechen. Eine (volatile) boolsche Variable brauchst du natürlich weiterhin.
herbivore
Okay das mit dem abbrechen das habe ich verstanden. Wie aber soll ich damit die "Pause" erreichen? Kannst du das bitte noch mal ein bisschen genauer Erklären?
Again what learned...
Hallo rollerfreak2,
durch den Timeout. Also ich finde das alles sehr offensichtlich und ärgere mich ehrlich gesagt etwas über die in meinen Augen überflüssigen Nachfragen. Das Prinzip habe ich beschrieben und alles weitere kannst du der Doku entnehmen.
herbivore
Hallo herbivore,
ich finde das überhaupt nicht offensichtlich. Desweiteren "denke" ich das ich falsch verstanden wurde.
wenn man in einem Vorgang eine bestimmte Zeitspanne warten will, den Vorgang aber auch während dieser Zeitspanne abbrechen können will...
Das Wort während ist falsch, es ist zu jedem Zeitpunkt der Ausführung möglich die GetModi Mehtode abzubrechen. Ich glaube unten stehendes Diagramm verdeutlicht das was ich erreichen will etwas. Nehmen wir mal nur die Sync methode, diese kann auch nicht abgebrochen werden! Den Aufruf der GetModi sync findet im GUI Thread statt. Laut deiner Aussage ist das pollen dort nicht mehr notwendig wenn man die Denkweise umdreht.
den Vorgang aber auch während dieser Zeitspanne abbrechen können will, bietet es sich an, die Sache gedanklich umzudrehen, also vorrangig auf den Abbruch zu warten, aber das maximal eine bestimmte Zeitspanne lang, sprich z.B. einen AutoResetEvent mit TimeOut zu verwenden. Damit vermeidet man jede Art von Polling
Leider hab ich keinen Plan wie das gehen soll, wenn man in der im Diagramm dargestellten Methode eine Pause machen soll, ohne zu pollen?
Again what learned...
Hallo,
zeichnen im Bild noch ein wo überall abgebrochen werden kann. So wie das Bild jetzt da ist wärs genau mein obiger Code.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
So wie das Bild jetzt da ist wärs genau mein obiger Code.
Welchen obigen Code meinst du denn? Wenn du den Task<TResult> meinst, im PDF unter for the future, den Task kann ich nicht verwenden => .NET 2.0.
Das unterbrechen der Methode durch den User kann nur erfolgen, wenn die Methode Async aufgerufen wird. Das unten stehende Diagramm zeigt den Async aufruf. Dort kann die Methode zu jeder Zeit beendet werden. Daher immer dann wenn das aborted flag gesetzt wird soll der Thread beendet werden.
Again what learned...
Hallo,
Welchen obigen Code meinst du denn?
Es gibt eh nur einen der bisher gepostet wurde und nicht von dir ist 😉
Aber da kannst du gleich Thread.Sleep nehmen, denn mehr macht obiger Code bei genauer Betrachtung auch nicht wirklich.
Der Unterschied zu herbivores Vorschlag oben ist also dass jederzeit abgebrochen werden soll und nicht während des Wartens. Korrekt?
Die Bilder hättest du eingangs posten könne dann wärs gleich klarer gewesen 😉
Da du schreibst dass die async Methode gleich der sync Methode ist und ein Callback mitgegeben wird kann die Implementierung für beide gleich sein und der async Aufruf geht über Delegate.BeginInvoke - dort auch das Callbakc angegeben.
Ist es möglich das Abbrechen so handzuhaben dass beim Abbruch die asynchrone Ausführung normal zu Ende läuft aber dessen Ergebnis (sprich das Callback) ignoriert / nicht verwendet wird?
Oder wenns möglich ist dass am Ende der Methode 1x geprüft wird ob abgebrochen wird oder nicht und dann entsprechend der Callback aufgerufen wird oder nicht. Dabei wären aber wieder die sync und async nicht ganz gleich bzw. der Delegat für den async-Aufruf müsste angepasst werden.
Sonst ist ein Abbruch eines Threads aus dem ThreadPool nicht möglich* - ich meine wirklich Abbruch. Das ginge nur sehr unsauber über Thread.Abort aber das sollte wenn schon nur für selbst erstellte Threads verwendet werden, und nicht mal dort.
* auch wenn du vor und nach jedem Metodenaufruf ein (volatile) bool prüfst wäre das nicht jederzeit - wenn man es genau nimmt.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Es gibt eh nur einen der bisher gepostet wurde und nicht von dir ist 😉
Sorry, den hab ich übersehen 😄
Der Unterschied zu herbivores Vorschlag oben ist also dass jederzeit abgebrochen werden soll und nicht während des Wartens. Korrekt?
So ist es aber das habe ich auch eingangs auch erwähnt und so geht es eigentlich auch aus dem ersten Code Post heraus dachte ich. Die Bilder hätte ich gleich anfangs schon mit posten können, das wäre sicher eleganter gewesen.
Ist es möglich das Abbrechen so handzuhaben dass beim Abbruch die asynchrone Ausführung normal zu Ende läuft aber dessen Ergebnis (sprich das Callback) ignoriert / nicht verwendet wird?
Nein das ist leider nicht möglich, weil der Service Disposed wird, daher soll auch der Thread "sofort", bzw. ziemlich bald beendet werden, damit der Service dispose aufruf zurück zum Aufrufer kehrt.
* auch wenn du vor und nach jedem Metodenaufruf ein (volatile) bool prüfst wäre das nicht jederzeit - wenn man es genau nimmt
Das ist mir klar, jedoch ist das besser, als am Thread.Abort aufzurufen.
Fazit, ob Timer oder Thread.Sleep ist eigentlich Schnuppe. Beim Thread.Sleep wird halt nicht gepolled.
Again what learned...
Hallo rollerfreak2,
ich verstehe überhaupt nicht, was du an meinem Vorschlag nicht verstehst. Natürlich löst der dein Problem.
wenn man in einem Vorgang eine bestimmte Zeitspanne warten will, den Vorgang aber auch während dieser Zeitspanne abbrechen können will...
Wie du einen Thread abbrechen kannst, weißt du doch, nämlich mit einer (volatilen) boolschen Variable. Deshalb habe ich dazu nichts geschrieben. Dein Problem war jetzt, wie man auch während einer "Pause" abbrechen kann. Wie wie man die Pause realisiert und bei Bedarf abbrechen kann, habe ich beschrieben. Das kombiniert mit der (volatilen) boolschen Variable (direkt hinter der Pause sowie an allen anderen Stellen wo du es sowieso schon hattest), löst dein Problem. Ich verstehe nicht, was daran unverständlich sein soll, zumindest nachdem ich gesagt habe, dass du die (volatile) boolsche Variable natürlich weiterhin brauchst. Und wie schon gesagt, weder Timer noch Thread.Sleep sind hier angebracht.
herbivore
Hallo herbivore,
ich glaub jetzt hab ich verstanden wie das von dir Beschreibende Prinzip funktioniert. Zum einen bleibt die boolsche Variable bestehen, die zum beendet verwendet wird. Die kann nur gesetzt werden wenn die GetModi Methode in einem Thread ausgeführt wird, daher Async. Und nun einfach die Logik herum drehen, einfach ein WaitOne zu rufen, und im Falle die Methode soll abgebrochen werden, die boolsche Variable setzen und am AutoResetEvent Set aufrufen.
private void GetModiAsync()
{
//this method is executed in a separat thread
...
if (this.m_aborted) return;
...
if (this.m_aborted) return;
...
this.m_autoResetEvent.WaitOne(interruptTime);
this.m_autoResetEvent.Reset();
...
if (this.m_aborted) return;
...
}
public void ThreadAbord()
{
this.m_aborted = true;
//force the thread to stop the interrupt
this.m_m_autoResetEvent.Set();
}
Hat zwar lang gedauert bis ich es kapiert habe, aber besser später als nie 😄
Danke für den echt hilfreichen Ansatz.
Again what learned...
Hallo,
beim AutoResetEvent ist ein Aufruf von Reset nicht notwendig denn das passiert Auto im Gegensatz zum ManualResetEvent.
Den Code für GetModiSync und Async könntest du dann noch in eine Methode auslagern und per Flag mitteilen ob sync/async ist und dann halt nicht auf das WaitHandle warten oder eben doch. DRY.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
beim AutoResetEvent ist ein Aufruf von Reset nicht notwendig denn das passiert Auto im Gegensatz zum ManualResetEvent.
Hab ich auch grad gesehen, das wird beim Aufruf von WaitOne automatisch gesetzt.
Den Code für GetModiSync und Async könntest du dann noch in eine Methode auslagern und per Flag mitteilen ob sync/async ist und dann halt nicht auf das WaitHandle warten oder eben doch
Der Interrupt muss IMMER sein, daher sowohl bei Sync als auch bei Async!
Again what learned...
Hallo,
hab mich vertan. Meinte das Abbrechen -> if (async && m_aborted)
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
die Abfrage nach if (async && m_aborted) ist unnütz. Das m_aborted flag ist im Sync Falle sowieso immer auf false. Und im Async Fall ist es halt entweder false, oder true. Von daher ist die "zusätzliche" abfrage nach dem sync/async flag nicht notwendig bzw. überflüssig.
Again what learned...