Go Down

Topic: Unbeabsichtigter Aufruf einer virtuellen Methode. (Read 1 time) previous topic - next topic

combie

#30
Jul 13, 2019, 02:28 pm Last Edit: Jul 13, 2019, 04:05 pm by combie
Quote
Combie hat die beste C++ Version erstellt, die ich bisher gesehen habe - deutlich besser, als meine eigenen Versuche dazu.

Glückwunsch - weiter so! ;)
Danke für die Blumen und den Zuspruch!

Allerdings, ist es doch auch ein hervorragender Dünger für meinen Hochmut und Arroganz. :o


Kritik, Bemerkungen?
Men los!

Ich fange mal an:
Beim überfliegen deiner Seiten, ist mir ein "sind" aufgefallen, welches, so vermute ich, eigentlich gerne ein "Sinn" werden möchte.

---

Eigentlich höre ich mir gerne alles an....
Etwas Probleme habe ich da mit allzu absoluten Aussagen, und (ausuferndem) Featurerismus.

Offensichtlich hast du dich schon "etwas" mit dem Thema Multitasking beschäftigt.
Das Denken in Nebenläufigkeiten scheint mir, bei dir, gut verankert zu sein.
Und der Einbau deiner "TaskHandles", in meinen Code, hat ja auch gut geklappt.

Wie auch immer...
Ich betrachte diese Multitasking Variante eher als Fingerübung in Sachen C++, als ein bezugsfertiges System.
Da ist also noch deutlich Entwicklungspotential.

Also:
Bitte raus damit.
Sage, was es zu sagen gibt.


Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

Doc_Arduino

#31
Jul 14, 2019, 05:21 pm Last Edit: Jul 14, 2019, 05:21 pm by Doc_Arduino
Hallo,

ich habe es heute probiert ohne das Board Paket 1.6.209.
Alle Zusatzpfade in den Preference gelöscht und IDE deinstalliert.
Auch den Ordner  C:\xyz\Worker\AppData\Local\Arduino15  habe ich anschließend gelöscht.
Nackte aktuelle IDE installiert.
Wenn man die platform.local.txt dann in den Pfad
C:\Program Files (x86)\Arduino\hardware\arduino\avr\ ablegt verwendet die IDE den dort angegebenen gcc 9.1.
Ein Problem weniger.  :)



Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

heweb

#32
Jul 14, 2019, 11:34 pm Last Edit: Jul 15, 2019, 07:33 pm by heweb
Ok, Du hast es gewollt ;)

1) Es gibt keinen TaskStatus. (Task.h: enum state {READY, DELAYED, ..} class Task { state State; ... }

Das führt dazu, dass auch pausierende Tasks aufgerufen werden, die - da pausierend - sofort mit taskSwitch:
Code: [Select]

#define taskSwitch()                               \
    do {                                                     \
        lable = &&LC_CONCAT(taskLable, __LINE__);    \
        return;                                                                    \
       LC_CONCAT(taskLable, __LINE__):;                    \
    } while(0)


#define taskPause(Task_interval)                                  \
    timeStamp = millis();                                                 \
    while((millis() - timeStamp) < (Task_interval)) taskSwitch()



zurückzukehren. Welch eine Verschwendung. Bei länger wartenden Tasks kann sich das millionenfach wiederholen, so dass 20 wartende Task einen Task, der jetzt dran ist, erheblich verzögern können.
Ergebnis: 200000 TaskSwitches /s aber dabei nur echte 2 TaskSwitches für READY Tasks.
Ein Task, der pausiert, darf nicht aufgerufen werden! Punkt.

Bei RTOS ( die, die mit Listen arbeiten) wird ein pausierender Task auf eine andere Liste verschoben.

2) Die Verwaltung mit der Liste ist fragwürdig. Es wird nur zugefügt. Das geht besser (und schneller) mit einem
Array[MAXTASKS];
Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht.

3) Und wann und wie lösche ich einen Task? Jederzeit? Oder nur bei einem bestimmten Zustand seiner selbst?

4) Es fehlen Zustandüberwachungen wie: (serial output):
Starte Task nur, wenn Resource frei ist.
Oder: Bleibe blockiert, bis ein anderer Task (oder Interrupt) dich wieder aktiviert.

5) Es fehlen Taskprioritäten, um dafür zu sorgen, dass der wichtigere (READY-) Task zuerst aufgerufen wird.

6) Die Tasks (A,B,C) werden "rückwärts" initialisiert und auch (bei gleichen Delays) abgearbeitet.
Das kann bei Tasks, deren Wirkung - gerade beim Start - aufeinander aufbaut, zu Problemen führen:

Sch-Begin: BlinkTask begin, pin: 5 Name: C                    <<<
Sch-Begin: BlinkTask begin, pin: 12 Name: B                  <<<
Sch-Begin: BlinkTask begin, pin: 2 Name: A                    <<<
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: F                                    
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: Hallo1
Loop Task: Hallo1 Pin:14                                                <<< Reihenfolge rückwärts
Loop Task: F Pin:14                                                        <<< ...
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2

Folge von: An Liste (add(Node)) wird vorne angefügt - nicht hinten!


Mit anderen Worten: als "Fingerübung" sehr schön - in der Praxis so (noch!) nicht zu gebrauchen.
Also: Tasks haben Zustände (state), die vom Scheduler berücksichtigt werden:
Code: [Select]

For i < TaskAnzahl

    if (DELAYED)
      Test, ob von DELAYED auf READY

    if (READY)
      call Task

End For  

Eine solche Schleife übergeht z.Bsp. auch BLOCKED Tasks ohne Zeitverlust.

Warum taskPause in millis() statt micros(), wenn 200000 TaskSwitches ausgeführt werden (alle 5 us) ?

Jeder Task muss von class Task ableitet werden. Warum ? Es reicht, wenn class Task die Funktionspointer für
       begin, loop
enthält. Wobei loop eigentlich reicht.
Dann können mit TaskInit("name", BeginPt, LoopPt); Funktionen vom Scheduler aufgerufen werden - wesentlich übersichtlicher. Alle Tasks sind von class Task - egal, was sie letztendlich tun!

Tasks können als einfache Funktionen (mit taskBegin, taskEnd)  deklariert sein - wesentlich übersichtlicher und einfacher, als für jeden neuen Task eine neue Klasse zu definieren/abzuleiten.
Sinn von OOP ist es schließlich, Dinge zu abstrahieren und die Benutzung des Gesamtproduktes zu vereinfachen, statt sie unnötig zu komplizieren.

Da aber TaskLoop sowieso vom Scheduler aufgerufen wird, ist Task.h eigentlich auch überflüssig:
Es reicht struct (von mir aus class) Task:
Code: [Select]

class task {
    char     *name;
    char     ID;
    state    State;
    void     *Param;
    void     (*FunctionLoop)();  // Von InitTask besetzt
...
}


Damit kann - wenn gewünscht - auch eine Parameter-Struktur an einen aufgerufenen Task weitergereicht werden. Die auch von mehreren Tasks benutzt werden kann.

Bitte nicht böse sein. Ich finde Deinen Ansatz nach wie vor Klasse! Aber etwas lösen von den "Fingerübungen" und mehr die Praxis im Hinterkopf haben könnte Sinn machen.

Hier eine simple Aufgabe:
BlinkTask1 : Loop: schalte LED 1000 us HIGH, dann 1000 µs LOW
BlinkTask2 : Wenn Blink2 auf LOW geschaltet hat - und nur dann:
               Loop: schalte LED 100 us High, dann 100 us LOW.

Lasse 5 weitere Tasks laufen und kontrolliere das Ergebnis mit einem LA:
Wie genau werden die Zeiten eingehalten? Wie lange braucht Task2, um auf die LOW Flanke von
Task1 zu reagieren?

Eine gute Intertask-Kommunikation ist sehr wesentlich für ein solches System. Reine Delays sind die Ausnahme!

Ich hoffe sehr, ich habe eher Deinen Ehrgeiz angestachelt, als Dich entmutigt.
Bleibe am Ball - Du bist auf einem guten Weg - der noch weit ist. Durchhalten ;)

Mit besten Wünschen und Grüßen

heweb

#33
Jul 14, 2019, 11:41 pm Last Edit: Jul 14, 2019, 11:48 pm by heweb
Wenn Du eine entsprechende Version machst:

Ich teste sie mit Vergnügen!

combie

Wenn Du eine entsprechende Version machst:
Das kannst du knicken!


Mit diesen Anforderungen, habe ich irgendwie gerechnet.

Irgend eine Art von abgesetzten Taskkontrollblöcken, werde ich nicht einführen.
Prioritäten, no
Tasks in einem Array fester Größe verwalten, no.

Ob eine Task abgelaufen ist, wen interessiert es...
Und wenn doch, dann kann sie ja ihren Status bereit stellen.
Oder sich jederzeit aus dem Speicher entfernen.

Was interessieren mich µs? ,
ms ist in meiner kleinen Arduino Welt meistens völlig ausreichend.
Zudem: Der Umbau, oder das hinzufügen, wäre trivial.

Du möchtest bei jedem Taskwechsel:
1. Prioritäten abhandeln
2. Taskzustand abhandeln
Das kostet RAM, Flash und Zeit.
Diese verplemperten Ressourcen könnte man effektiver für eine weitere Task verwenden.



Wer Spaß an der Featureritis hat , darf den Kram gerne einbauen.
Alternativ könnte der Wünscher dein System verwenden, oder FreeRTOS
Eine ganze Reihe von Alternativen, es gibt.


----------------------


Was anderes, ist die Sache mit den Mutex/Semaphore.
Ein solches Konzept ist manchmal nötig.
Allerdings auch bei jedem anderen System, welches Nebenläufigkeiten nutzt/implementiert.
Egal, ob da nun Multitasking auf dem Etikett steht, oder nicht.

Das kann/darf gerne jeder selber entwickeln.
Nutzbare Implementierungen gibt es auch schon wie Sand am Meer.
Aus dem Pool bediene ich mich sowieso schon.

Die Mutex/Semaphore Idee muss also nicht Teil "meines" Systems sein.
Und ja, es wäre ein sinnvolles/nützliches Feature.


----------------------------------------


Quote
2) Die Verwaltung mit der Liste ist fragwürdig. Es wird nur zugefügt.
Es wird auch entfernt.


Quote
Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht.
Instanzen von Task können unterschiedlich groß sein.


Quote
Damit kann - wenn gewünscht - auch eine Parameter-Struktur an einen aufgerufenen Task weitergereicht werden. Die auch von mehreren Tasks benutzt werden kann.
Das geht bei mir per Konstruktor, Template oder Setter Injektion.
Einen vierten Weg halte ich für flüssiger als Wasser. Für Überflüssig.


Quote
Tasks können als einfache Funktionen (mit taskBegin, taskEnd)  deklariert sein - wesentlich übersichtlicher.
War es nicht der OOP Ansatz, welche die Begeisterung bei dir ausgelöst hat?



Quote
Ein Task, der pausiert, darf nicht aufgerufen werden! Punkt.
Wie sagte ich zu absoluten Aussagen?
> Etwas Probleme habe ich da mit allzu absoluten Aussagen ....


Übrigens, wenn ich das richtig gesehen habe verwendest du switch/case für dein Konzept.
Das wird mit zunehmender Anzahl Fälle schnell sehr ineffektiv.
Es zerfällt in speicherintensive lookup tables oder zeitintensive Vergleichs/Entscheidungs Kaskaden.
Auch verbietet es die Verwendung von switch/case in Tasks.

Quote
Welch eine Verschwendung.
Nur wenn man das switch/case dafür verwendet.
Mit dieser simplen Anwendung von computed goto  ist man die Verschwendung los.



----


Ich hoffe, dass du mir jetzt nicht allzu böse bist.

Schade, dass meine Implementierung nicht deinen Ansprüchen genüge tut.
Aber sie wäre nicht so herzerfrischend naiv, wenn sie nicht so wäre, wie sie ist.


----


Wo es Potential gibt:
Die Wire und SPI Implementierungen blockieren, solange eine Transaktion läuft.
Da bleibt richtig Zeit liegen....

Serial ist da etwas komfortabler, es sagt immerhin, wie viele Zeichen/Byte im Buffer frei sind. So kann man Wartezeit abgeben.








Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

combie

#35
Jul 15, 2019, 11:48 am Last Edit: Jul 15, 2019, 12:55 pm by combie
Den Punkt 6 habe ich eben erst gesehen.....

Quote
6) Die Tasks (A,B,C) werden "rückwärts" initialisiert und auch (bei gleichen Delays) abgearbeitet.
Das kann bei Tasks, deren Wirkung - gerade beim Start - aufeinander aufbaut, zu Problemen führen:

Sch-Begin: BlinkTask begin, pin: 5 Name: C                    <<<
Sch-Begin: BlinkTask begin, pin: 12 Name: B                  <<<
Sch-Begin: BlinkTask begin, pin: 2 Name: A                    <<<
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: F                                   
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: Hallo1
Loop Task: Hallo1 Pin:14                                                <<< Reihenfolge rückwärts
Loop Task: F Pin:14                                                        <<< ...
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2

Folge von: An Liste (add(Node)) wird vorne angefügt - nicht hinten!
Deine Argumentation zieht nur, wenn man heimliche, von der Reihenfolge abhängige, Abhängigkeiten einbaut.
Aber wer das tut, bringt sich selber in des Teufels Küche.
Sowieso.

Aus meiner Sicht, eine irrationale Geschichte.


Ob an den Anfang, oder ans Ende der Liste eingefügt wird, ist prinzipiell völlig egal.
Die Funktionsfähigkeit eines Multitasking Systems darf nicht von der Abarbeitungsreihenfolge der Tasks abhängig sein.
Es müssen alle Tasks an die Reihe kommen, diese Garantie reicht.


Das Einfügen an den Anfang ist einfacher und deutlich schneller, bei einer einfach verketteten Liste.
Hinten einhängen und von Vorn durchlaufen, erfordert eine doppelt verkettete Liste.
Und damit quasi den doppelten Verarbeitungsaufwand bei Veränderungen und den doppelten Speicher Aufwand.

Das nur, um eine potentielle Schlampigkeit/Irrtum des Anwendungsprogrammierers auszubügeln?


Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

heweb

#36
Jul 15, 2019, 02:24 pm Last Edit: Jul 15, 2019, 09:58 pm by heweb
"Allerdings, ist es doch auch ein hervorragender Dünger für meinen Hochmut und Arroganz. :o "

Es war zu befürchten, dass Du so reagierst. ;)

"Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht."

Re. "Instanzen von Task können unterschiedlich groß sein."

Die von TCBs nicht !

"Du möchtest bei jedem Taskwechsel:
1. Prioritäten abhandeln
2. Taskzustand abhandeln
Das kostet RAM, Flash und Zeit.
Diese verplemperten Ressourcen könnte man effektiver für eine weitere Task verwenden. "

Oder für sinnlose TaskSwitches ?

Erweitere mal dein Macro taskPause

Code: [Select]

#define taskPause(Task_interval)                                      \
    timeStamp = millis();                                                      \
    while((millis() - timeStamp) < (Task_interval)) {             \
        Serial.println("Ich verschwende Zeit");                      \
        taskSwitch();                                                             \
    }


und lass es laufen und wundere Dich.

Um überhaupt noch etwas zu sehen nimm
Serial.print("."); \

Loop Task: A Pin:2
...7086
...Loop Task: E Pin:14
.Loop Task: D Pin:13
.................................................

(Hier hat jemand die unerträglich vielen Punkte gelöscht, die in taskPause geschriebben wurden,
Also selber ausprobieren!)



Ich bleibe dabei: diese taskPause() - ein zentraler Punkt -  ist wirklich reine Zeitverschwendung.


"Was interessieren mich µs? ,
ms ist in meiner kleinen Arduino Welt meistens völlig ausreichend."

Die Zahl der erreichten TaskSwitches/s scheinen Dich doch sehr zu interessieren, denn von denen berichtest
Du gerne ;)
Ein Standard Sensor: HC_SR04 Ultraschallsensor ... mit ms ?


Alle preemptiven Versionen sind langsamer und brauchen viel mehr Speicher. Genau da liegen ja die Vorteile des
kooperativen Multitasking.
Und selbst in der noch viel kleineren AtTiny Welt sind us oft interessant.

Auch wenn Du es anscheinend nicht merkst: Du benutzt selbst Duff's Device. (TaskBlock, taskSwitch, ...) mit
entsprechenden Auswirkungen:
Z. Bsp.: keine localen Variablen im Task möglich - nur static.

Im loop von BlinkTask:
Code: [Select]

virtual void loop()
 {
 static int mycnt;
   TaskBlock
   {
     mycnt++;
     Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.print(pin); Serial.print(" ");
     Serial.println(mycnt);

Mit static geht, ohne nicht!

Code: [Select]

oder probiere:
virtual void loop()
 {
   TaskBlock
   {
     int i=0;
     do {
       i++;
       taskSwitch();
     } while (i<5);

     // wird NIE ausgeführt !!!        
     >>>>> Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.println(pin);


Ich habe mal taskSwitch/taskPause erweitert:

Code: [Select]

// Task Kontroll Worte, diese werden Taskwechsel einleiten
#define taskSwitch()                                                          \
  do {                                                                                  \
        SwitchCount++; \
        lable = &&LC_CONCAT(taskLable, __LINE__);        \
        return;                                      \
        LC_CONCAT(taskLable, __LINE__):;                        \
  } while(0)
  
#define taskPause(Task_interval)                                      \
  timeStamp = millis();                                                        \
  while((millis() - timeStamp) < (Task_interval)) {               \
      PauseCount++;                                                           \
      digitalWrite(5, HIGH);                                                  \
      taskSwitch();                                                               \
      digitalWrite(5, LOW);                                                  \
  }


Ein spezieller BlinkTask2 gibt alle 2 Sekunden aus:

Code: [Select]

TaskBlock
    {
      Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.println(pin);
      Serial.print("Micros/Pause/Switch:  ");
      Serial.print(micros()); Serial.print(" / "); Serial.print(PauseCount);
      Serial.print(" / "); Serial.println(SwitchCount);
      SwitchCount=0; PauseCount=0;


Output:
Loop Task: Hallo1 Pin:14
Micros/Pause/Switch:  535208045 / 440083 / 440083
Loop Task: F Pin:14
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2
Loop Task: E Pin:14
Loop Task: D Pin:13
31450
Loop Task: E Pin:14
Loop Task: D Pin:13
31203
Loop Task: Hallo1 Pin:14                              (TaskSwitch)
Micros/Pause/Switch:  537208039 / 438550 / 438550
                                                   (TaskPause)
Loop Task: Hallo1 kommt sehr sauber alle 2 Sekunden.

Aber:

taskPause() und in Folge taskSwitch() werden je 220000 mal pro Sekunde aufgerufen !

Das ist das, was ich mit Verschwendung meinte!

Auch mit dem LA kann man sehen:
Das System ist mehr als 50% der Zeit mit
              taskPause() / taskSwitch()
beschäftigt !

"Die Zeit kann man besser für weitere Tasks nutzen" ;)


Meine Implementierung erfüllt diese Forderungen und ist mindestens so schnell, wie Deine ;)
Bei vielen Tasks mit delay mit Sicherheit schneller. Ich stelle mich gerne einem Vergleich.
Es geht also! Wo bleibt Dein Ehrgeiz?

Wie gehst Du im Job mit einem Vorgesetzten um, der Forderungen wie ich stellt?

Schade - Dein grundsätzlicher Ansatz hätte viel Potential gehabt. Aber so bleibt es wohl doch nur Hilfsmittel für Hochmut und Arroganz  - und sicher wärst Du stinksauer, wenn ich sagen würde, was ich davon halte.
So jedenfalls ist Dein Produkt nicht brauchbar.

Auf den Ansatz kannst Du stolz sein - auf das sture Verfolgen der Irrwege und Verkaufen von Anfängerfehlern als Innovation eher nicht.

Schade, denn ich hätte Dir wirklich das Potential zugetraut, ein beachtliches Produkt zu schaffen! Aber wenn Du so schnell aufgibst mit "will ich nicht, weil ich es nicht kann", dann wird die Säule des Hochmuts wohl ins Wanken geraten.

Schluck einfach mal trocken runter und schaue - ohne beleidigt zu sein - meine Ratschläge noch einmal in Ruhe an. Meine Beispielaufgabe ist doch wirklich nicht schwer. (Die beiden HIGH/LOW Tasks)
Programmieren ist kein Selbstzweck - es sollte auch als Übung etwas brauchbares leisten.

Und solltest Du je einmal professioneller Programmierer werden wollen - gewöhne Dich daran, mit Anforderungen umzugehen ;)
Ich habe Dir ein paar Jahrzehnte Praxis voraus. Das heißt nicht zwangsläufig, dass ich ein besserer Programmierer bin. Aber ich sehe sehr schnell, wo es bei einer Lösung klemmt. Im Zweifel mit einem LA - ohne das Programm auch nur zu sehen.
Was hindert Dich, in class Task ein flag zu setzen: InDelay und timestamp dort zu deklarieren und in
class Scheduler::loop:
Code: [Select]

while(temp)
{
     temp->loop();  // InDelay neu setzen
     temp = (Task *)list.getNext(temp);

     // Dieser Test sorgt dafür, dass ein Task, der pausiert, gar nicht erst
     // Aufgerufen wird. Eine enorme Zeitersparnis!  

     if (temp->InDelay) {
         if ((millis() - timeStamp) < (Task_interval)) {    // Dieser Task pausiert noch
             temp = (Task *)list.getNext(temp);              //  Der nächste bitte ...
         }
     }
     else temp->InDelay = false;
}

// Nicht vollständig, aber ich denke die Richtung ist erkennbar.

Damit würden nur Tasks aufgerufen, die nicht pausieren - READY Tasks also viel schneller drankommen !

(Nachtrag)
Schaue dir mal das Disassembly von switch/case und computed goto an. Keiner zu finden?
Doch: Switch/case arbeitet genauso mit den vom Compiler  __LINE__ generierten Labels.
Ein Fehler darin führt zu Fehlverhalten (der case wird nicht gefunden) - aber nicht zum Absturz.
Und dieses Fehlverhalten kann entdeckt und abgefangen werden. (Mache ich auch nicht, aber es geht)
Computed goto verreckt sofort gnadenlos ! Still ruht der See.

Sturheit ist manchmal eine Tugend - und manchmal einfach nur dumm.
Die Kunst ist, die beiden Fälle zu erkennen ;)

Schaffst Du es, mir trotz meiner klaren Worte nicht böse zu sein?




heweb

#37
Jul 15, 2019, 02:57 pm Last Edit: Jul 15, 2019, 07:54 pm by heweb
Die Reihenfolge der Tasks  kann am Anfang (ja, nur da!) wichtig sein. Z. Bsp. zur Initialisierung des Systems mit einem Task "SysInit", der weitere Tasks, die geblockt starten, freigibt.
Oder auch:
So etwas benötigt man z. Bsp., wenn ein WDT kontrolliert einen Systemfehler erkennt und SysInit später als Task Aufrufen möchte - der wiederum das System hochfährt.
Es ist auch wichtig, wenn sich 2 Tasks Signale senden und so gegenseitig synchronisieren, aber nur, wenn sie in passender Synchronisation starten:
Task_1 sendet Ultraschallpuls und startet Task2.
Task2 misst die Zeit des Echos.
Wer keine Taskkommunikation benutzt und die Tasks "anonym" frei und losgelöst von dieser Welt und ohne eine eigentliche Funktion "vor sich hinblinken läßt", dem kann die Start-Reihenfolge natürlich egal sein :)
Mit der Meinung: "Die Startreihenfolge ist unwichtig" wirst du wohl nicht viele überzeugen.
Da tropft es nämlich aus der Kaffeemaschine, bevor der Startknopf gedrückt wurde.

Ich denke, wenn Du ausser LED-Geblinke auch mal richtige Beispiele programmierst, wird dir so mancher Kronleuchter aufgehen.
Übrigens: Mit absoluten Aussagen habe ich nicht so Probleme, wie Du. Aber mit absolutem Unsinn schon.
Denn Programieren bedeuted Logik - und das Programm MACHT absolute Aussagen.
Also: mal ran an die Kaffemaschine:
Ist Einschaltknopf gedrückt?
Ja, ist genug Wasser da? Nein, blinke uns stoppe.
loop: Ja, heize Wasser auf
Wasser heiß genug? Nein, gehe zu loop
...
Jeder Task ist von dem anderen abhängig.
Wasserbehälter wird unvorhergesehen entnommen. Also alles stoppen und blinken.

Aber ich habe den Eindruck, Du bist schon so arrogant, dass Du lernresistent geworden bist !
Schade.



heweb

Denn Hochmut kommt vor dem Fall - immer  :smiley-eek-blue:

Wer nicht in der Lage ist- auch nicht mit einem Serial.print - festzustellen, welche Taskvariation nun gerade blinkt
Wer im Delay die millis() bis zum Abwinken pollt und damit den Prozessor massiv belastet
Wer Tasks baut, deren Status (READY, BLOCKED, DELAYED) nicht erkennbar ist
Wer Sicherheitsabfragen (new: hat es geklappt?) für überflüssig hält
Wer wirklich glaubt, die Reihenfolge beim Start von Tasks sei egal
Wer sich an das Publikum mit einem Problem wendet, weil die REIHENFOLGE bei der Initialisierung nicht stimmte

der sollte sich sagen lassen: Das Üben mit Tools macht Sinn. Die Demonstration seiner Fertigkeit im Umgang damit auch. Aber es nützt nichts, wenn es nicht irgendwann auch einmal verwertbar eingesetzt wird.
Auch wenn Du bei jedem Jahrmarkt einen Nagel immer mit drei Schlägen versenkst - ein guter Zimmermann bist Du deshalb noch nicht.

heweb

Ich habe Dein defektes Programm (QuadroBlinkDynamicDefekt) repariert.
Dabei hätte ich es belassen sollen.
Der Rest war wohl Perlen ...

Tommy56

@heweb: Das was Du hier abziehst, hat aber auch nichts mehr mit sachlicher Kritik zu tun. Du hast Dich in eine Hasspredigt rein gesteigert.

Komm wieder runter von Deinem hohen Ross.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

heweb

#41
Jul 15, 2019, 03:37 pm Last Edit: Jul 15, 2019, 09:26 pm by heweb
"Das Einfügen an den Anfang ist einfacher und deutlich schneller, bei einer einfach verketteten Liste.
Hinten einhängen und von Vorn durchlaufen, erfordert eine doppelt verkettete Liste.
Und damit quasi den doppelten Verarbeitungsaufwand bei Veränderungen und den doppelten Speicher Aufwand."

Tut mir leid, auch das ist Unsinn!

Deine Version - zufügen am Anfang:

Code: [Select]

void add(Node *client)
  {
    Node *temp = first;
    first = client;
    client->next = static_cast<ClientClass *>(temp);
  }


Ein Pointer auf den letzen Eintrag reicht völlig aus.

Meine Version - zufügen am Ende:

Code: [Select]

void add(Node *client)
{
  if (end) end->next = static_cast<ClientClass *>(client);
  else first=client;
  end = client;
}


Diese kleine Änderung - ohne jede sonstige Modifikation im Programm - sorgt für Abhilfe.
Einen großen Mehraufwand an Zeit und Code sehe ich nicht.

Kannst Du wenigsten hier mal zugeben, Unrecht gehabt zu haben ? ;)

heweb

@Tommy56
Hasspredigt ist wohl doch übertrieben.
Wenn jemand übt, dann helfe ich gerne. Aber mit Hochmut und Arroganz habe ich so meine Probleme - wenn sie auf sehr wackeligen Füssen stehen.
Ja, ich gebe zu, so schlau wie mit 18 bin ich nie wieder geworden ;) Und das ist das geschätzte Alter von Combie.
Aber wenn er anfängt, vor lauter Selbstüberschätzung gute und in der Praxis bewährte Produkte herabzusetzen
und seine Methoden als die Weisheit dieser Welt hinstellt - und zwar sehr absolut - und die Begründung dogmatisch wird, statt logisch, dann wird es problematisch.
Ich habe nichts dagegen, wenn er mir bessere Methoden zeigt - es gibt immer einen, der besser ist.
Aber es muss dann auch Hand und Fuß haben. Ich bin da für Beispiele, Tests, Messugen usw.

Ich denke - mein letztes Beispiel (Liste) zeigt sehr genau, was ich meine.

Tommy56

#43
Jul 15, 2019, 03:51 pm Last Edit: Jul 15, 2019, 03:53 pm by Tommy56
Wenn Du schon weiter hier rumtrollen willst, dann zeige wenigstens, dass Du die Minimalforderungen an eine Diskussion in einem Forum erfüllen kannst: Kennzeichne Zitate ordentlich als solche und setze Deine Codefragmente in Codetags.
Deinen anmaßenden Ton und die Beleidigungen kannst Du auch weglassen.

Gruß Tommy

Edit: Du hast Probleme mit Arroganz? Dann schaue mal in den Spiegel.
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

combie

#44
Jul 15, 2019, 03:53 pm Last Edit: Jul 15, 2019, 03:54 pm by combie
Quote
Du bist schon so arrogant, dass Du lernresistent geworden bist !
Da hast du natürlich völlig Wahr!
Lernresistenz und Arroganz, sind meine herausragendsten Eigenschaften.


Ich wünsch dir viel Spaß mit deinen tropfenden Kaffeemaschinen und auch (egal wie verketteten) Listen.


Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

Go Up