Asynchrone und mehrfädige Programmierung in MQL - Seite 36

 
Vict:

Die einzige vernünftige Erklärung, die mir in den Sinn kommt, ist die manuelle Kontrolle über die Anzahl der im Pool laufenden Threads (wenn wir nicht auf die Standardeinstellung async|deferred vertrauen) - zum Beispiel, wenn wir sehen, dass das System stark belastet ist, senden wir Aufträge mit async-Flag erneut, senden sie deferred.

Im Allgemeinen bin ich etwas enttäuscht von async() Trägheit, ich werde meine eigenen leichtgewichtigen Thread-Pool erstellen, es scheint mir, dass es viel schneller sein wird.

Der standardmäßige async|deferred Modus wird im Buch so beschrieben, dass die Aufgabe, wenn nicht genügend physische Ressourcen vorhanden sind, keinen neuen Thread erstellt, sondern im Hauptthread ausgeführt wird und diesen blockiert.
Und Sie sollten sich immer der möglichen Sperrung des Hauptthreads im Standardmodus bewusst sein, da Sie in den meisten Fällen Lösungen benötigen, um den Hauptthread nicht zu blockieren.
Das heißt, der Standardmodus schaltet je nach Auslastung einer physischen Ressource, z. B. des Prozessors, automatisch um, wo die Aufgabe ausgeführt werden soll.
Wenn wir sicher sind, dass die physische Ressource nicht überlastet wird, sollten wir daher das Flag std::launch::async explizit angeben.
Aber wenn man eine physikalische Ressource moderner Prozessoren überlaufen lässt, muss man nach solchen Berechnungen suchen, um ihr volles Potenzial zu erreichen ))
Ich kann nichts über die Geschwindigkeit sagen, weil ich die Theorie noch studiere ))

 
Roman:

Wenn wir also sicher sind, dass die physische Ressource nicht überlaufen wird, ist es besser, das Flag std::launch::async explizit anzugeben.
Und zu überlaufen eine physische Ressource der modernen Prozessoren, dauert es eine lange Zeit, um solche Berechnungen zu finden, um alle potenziellen auszuwählen ))

Der Prozessor kann eine noch größere Anzahl von Threads verkraften, aber die Fähigkeiten des Betriebssystems werden eher zu einem Engpass. Nun, es kann nicht endlos Threads multiplizieren, früher oder später wird async(lauch::async, ...) auf eine geworfene Ausnahme stoßen.

 
Vict:

Der Prozessor kann sogar eine große Anzahl von Threads verkraften, aber die Fähigkeiten des Betriebssystems werden eher zum Engpass. Nun, es kann nicht endlos Threads multiplizieren, früher oder später wird async(lauch::async, ...) auf eine geworfene Ausnahme stoßen.

Ja, es gibt immer eine physikalische Grenze, aber es ist unwahrscheinlich, dass diese Grenze in unseren Aufgaben für mt5 zu überschreiten.
Auch async und future geben bei ihrem Rückgabewert Ausnahmen zurück, wenn sie auftreten, unabhängig davon, wie wir diesen Wert erhalten, über eine Lambda-Funktion, ref() oder .get().
Und std::thread kann in seinem Rückgabewert keine Ausnahmen zurückgeben.

 
Roman:

Ja, es gibt immer eine physikalische Grenze, aber in unseren Aufgaben für mt5, ist es unwahrscheinlich, über diese Grenze zu gehen.
Auch async und future geben bei ihrem Rückgabewert Ausnahmen zurück, wenn sie auftreten, unabhängig davon, wie wir diesen Wert erhalten, durch eine Lamda-Funktion, ref() oder .get().
Und std::thread kann in seinem Rückgabewert keine Ausnahmen zurückgeben.

Ich glaube nicht, dass Sie sich zu sehr über async aufregen sollten. Es scheint aus Bequemlichkeit gemacht worden zu sein, aber all diese Dinge scheinen die Leistung wirklich zu beeinträchtigen. Es ist nicht durch einen Pluspunkt.

Und std::thread im Rückgabewert kann keine Ausnahmen zurückgeben.

Das ist nicht immer notwendig. Aber wenn Sie das tun, brauchen Sie ein Dutzend zusätzliche Zeilen (auch wenn es schneller geht - ohne die ganze Zuweisung im Stapel).
 
Vict:
Aber wenn es sein muss, sind das ein Dutzend zusätzliche Zeilen (obwohl es schneller ginge - ohne die ganze Zuteilung im Stapel).

Um nicht unsubstantiiert zu sein:

#include <thread>
#include <future>
#include <iostream>
#include <chrono>
using namespace std;

template <typename T>
void thread_fn(T &&task) {task(0);}

int main()
{
   packaged_task<int(int)> task{ [](int i){this_thread::sleep_for(3 s); return i==0?throw 0: i;} };
   auto f = task.get_future();
   thread t{thread_fn<decltype(task)>, move(task)};
   t.detach();

   try {
      cout << f.get() << endl;
   }catch(...) {
      cout << "exception caught" << endl;
   }

   return 0;
}

Er brauchte nicht einmal ein Dutzend. Ja, Sie können noch mehr Low-Level-Zeug ohne die ganze packaged_task und Zukunft Zeug tun, aber die Idee ist, dass Ausnahmen zu werfen ist nicht einige super async Sache, und Thread hat absolut nichts.

 
Vielleicht wäre es aus praktischer Sicht manchmal sinnvoll, sich von all diesen Polstern zu lösen und sich an die Windows-Api zu erinnern - CreateThread, Synchronisierungsprimitive, verriegelte Funktionen. Trotzdem gibt es sie. Natürlich, wenn man es für die Winde schreibt. Warum die Dinge verkomplizieren, wenn MT4|MT5 keine so komplizierten Aufgaben haben, die verzögerte Berechnungen, Pools und so weiter erfordern.
 
Andrei Novichkov:
Warum die Dinge verkomplizieren, wenn es für MT4|MT5 keine komplexen Aufgaben gibt, die verzögerte Berechnungen, Pools usw. erfordern?
Es gibt tatsächlich Aufgaben. MT verfügt nicht über diese Möglichkeiten.
Hier sind alle Arten von Pools wirklich unnötig. Standard-Multi-Threading-Lösungen sind, wenn sie richtig angewendet werden, ausreichend.
 
Yuriy Asaulenko:
Die Herausforderungen sind tatsächlich da. Es gibt keine Möglichkeiten für MT.
Hier sind alle Arten von Pools wirklich unnötig. Standard-Multithreading-Lösungen sind, wenn sie richtig angewendet werden, ausreichend.
Das ist es, was ich meine.
 

Leute, ich teile meine Forschungsergebnisse.

Ich habe meinen eigenen Fadenpool auf meinen Knien geschrieben. Ich sollte anmerken, dass es sich um eine recht funktionale Version handelt, man kann einen beliebigen Funktor mit beliebigen Parametern übergeben, als Antwort wird future zurückgegeben, d.h. alle Plushkas in Form von Exception-Catching und Warten auf Beendigung sind verfügbar. Und es ist genauso gut wiehttps://www.mql5.com/ru/forum/318593/page34#comment_12700601.

#include <future>
#include <iostream>
#include <vector>
#include <mutex>
#include <set>

mutex mtx;
set<thread::id> id;
atomic<unsigned> atm{0};

int main()
{
   Thread_pool p{10};
   for (int i = 0;  i < 10000;  ++ i) {
      vector<future<void>> futures;
      for (int i = 0; i < 10; ++i) {
         auto fut = p.push([]{
                              ++ atm;
                              lock_guard<mutex> lck{mtx};
                              id.insert( this_thread::get_id() );
                           });
         futures.push_back(move(fut));
      }
   }
   cout << "executed " << atm << " tasks, by " << id.size() << " threads\n";
}

Ich weiß nicht, wer und in welchem Zustand std::async geschrieben hat, aber mein in den Knien entwickeltes Ding ist 4 Mal schneller als das Standardprogramm (mit 10 funktionierenden Threads). Die Erhöhung der Anzahl der Threads über die Anzahl der Kerne verlangsamt mich nur. Bei Poolgröße == Anzahl der Kerne (2) verliert async um etwa das 30-fache. So sieht es also aus.

Wenn ich Threads bündeln möchte, wird es sicher nicht standardmäßig asynchron sein)).

 
Vict:

Leute, ich teile meine Forschungsergebnisse.

Ich habe meinen eigenen Thread Pool auf meinen Knien geschrieben. Ich sollte anmerken, dass es sich um eine recht funktionale Version handelt, man kann jeden Funktor mit beliebigen Parametern übergeben, er gibt als Antwort die Zukunft zurück, d.h. alle Plushkas in Form von Ausnahmeabfang und Warten auf Beendigung sind verfügbar. Und ich habe sie ebenso genutzt wie https://www.mql5.com/ru/forum/318593/page34#comment_12700601.

Ich weiß nicht, wer und in welchem Zustand std::async geschrieben hat, aber mein in den Knien entwickeltes Ding ist 4 Mal schneller als das Standardprogramm (mit 10 funktionierenden Threads). Die Erhöhung der Anzahl von Threads über die Anzahl von Kernen verlangsamt mich nur. Bei Poolgröße == Anzahl der Kerne (2) verliert async um etwa das 30-fache. So sieht es also aus.

Wenn ich Threads bündeln möchte, wird es sicher nicht standardmäßig asynchron sein)).

Ich danke Ihnen für die Recherche. Es ist ein gutes Beispiel, etwas, worüber man nachdenken und daraus lernen kann.
Aber aus unserer allgemeinen Diskussion sind die meisten von uns zu dem Schluss gekommen, dass ein Strömungsbecken nicht wirklich notwendig ist.
In meinem Fall, das ist sicher, da ich erkannt habe, dass der Pool ist statisch in Bezug auf die Anzahl der Threads, es funktioniert nicht für mich.
Aber ja, wenn ich einen Pool brauche, dann ist Ihr Beispiel genau richtig. Danke für das Aufzeigen von Beispielen.
Ich bin noch dabei, den Dreh rauszukriegen ))