Programmation asynchrone et multithread dans MQL - page 36

 
Vict:

La seule explication raisonnable qui me vient à l'esprit est le contrôle manuel du nombre de threads en cours d'exécution dans le pool (si nous ne faisons pas confiance à l'async|deferred par défaut) - par exemple, si nous voyons que le système est fortement chargé, renvoyer les tâches avec le drapeau async, les envoyer deferred.

En général, je suis un peu déçu par la lenteur d'async(), je vais créer mon propre pool de threads léger, il me semble qu'il sera beaucoup plus rapide.

Le mode async|deferred par défaut est décrit dans le livre de sorte que si les ressources physiques sont insuffisantes, la tâche ne crée pas un nouveau thread, mais s'exécute dans le thread principal, en le bloquant.
Et vous devez toujours être conscient du verrouillage possible du thread principal en mode par défaut, car la plupart du temps, vous avez besoin de solutions pour ne pas bloquer le thread principal.
En d'autres termes, le mode par défaut change automatiquement l'endroit où exécuter la tâche en fonction de la charge d'une ressource physique, c'est-à-dire le processeur.
Pour cette raison, si nous sommes sûrs que la ressource physique ne sera pas surchargée, il vaut mieux spécifier explicitement le drapeau std::launch::async.
Mais si vous débordez une ressource physique de processeurs modernes, vous devrez rechercher de tels calculs pour atteindre son plein potentiel ;))
Je ne peux rien dire sur la vitesse, car j'étudie encore la théorie ;))

 
Roman:

Donc, si nous sommes sûrs que la ressource physique ne sera pas débordée, il est préférable de spécifier le drapeau explicitement std::launch::async.
Et pour déborder d'une ressource physique sur les processeurs modernes, il y a un long chemin à parcourir pour trouver de tels calculs afin d'en tirer tout le potentiel ;))

Le processeur peut supporter un nombre encore plus élevé de threads, mais les capacités du système d'exploitation risquent davantage de devenir un goulot d'étranglement. Eh bien, il ne peut pas multiplier indéfiniment les threads, tôt ou tard async(lauch::async, ...) se heurtera à une exception.

 
Vict:

Le processeur peut supporter même un grand nombre de threads, mais les capacités du système d'exploitation sont plus susceptibles de devenir un goulot d'étranglement. Eh bien, il ne peut pas multiplier indéfiniment les threads, tôt ou tard async(lauch::async, ...) se heurtera à une exception.

Oui, il y a toujours une limite physique, mais il est peu probable de dépasser cette limite dans nos tâches pour mt5.
De même, async et future renvoient des exceptions dans leur valeur de retour si elles se produisent, quelle que soit la manière dont nous obtenons cette valeur, via une fonction lambda, ref() ou .get().
Et std::thread dans sa valeur de retour ne peut pas renvoyer d'exceptions.

 
Roman:

Oui, il y a toujours une limite physique, mais dans nos tâches pour mt5, il est peu probable d'aller au-delà de cette limite.
De même, async et future renvoient des exceptions dans leur valeur de retour si elles se produisent, quelle que soit la façon dont nous obtenons cette valeur, par une fonction lamda, ref() ou .get().
Et std::thread dans sa valeur de retour ne peut pas renvoyer d'exceptions.

Je ne pense pas que vous devriez être trop excité par l'asynchronisme. Il semble avoir été fait pour la commodité, mais tous ces trucs semblent vraiment toucher les performances. Ce n'est pas par un plus un.

Et std::thread dans la valeur de retour ne peut pas renvoyer d'exceptions.

Ce n'est pas toujours nécessaire. Mais si vous le faites, c'est une douzaine de lignes supplémentaires (même si cela fonctionnera plus rapidement - sans toute l'allocation dans la pile).
 
Vict:
Mais si vous devez le faire, c'est une douzaine de lignes supplémentaires (alors que cela fonctionnerait plus vite - sans toute l'allocation dans la pile).

Pour ne pas être sans fondement :

#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;
}

Je n'ai même pas eu besoin d'une douzaine. Oui, vous pouvez faire encore plus de choses de bas niveau sans tous les trucs de packaged_task et de future, mais l'idée est que lancer des exceptions n'est pas un truc super asynchrone, et le thread n'a absolument rien.

 
Peut-être que, d'un point de vue pratique, il vaudrait parfois la peine de s'éloigner de tous ces coussins et de se souvenir de l'api Windows - CreateThread, primitives de synchronisation, fonctions verrouillées. Tout de même, il y en a. Bien sûr, quand vous l'écrivez pour les vents. Pourquoi compliquer les choses alors que MT4|MT5 n'ont pas de tâches aussi compliquées nécessitant des calculs différés, des pools et ainsi de suite.
 
Andrei Novichkov:
Pourquoi compliquer les choses alors qu'il n'existe pas de tâches complexes pour MT4|MT5 qui nécessitent des calculs différés, des pools, etc.
En fait, il y a des tâches. MT n'en a pas la capacité.
Ici, toutes sortes de piscines sont vraiment inutiles. Les solutions standard de multithreading, si elles sont appliquées correctement, sont suffisantes.
 
Yuriy Asaulenko:
En fait, les défis sont là. Il n'y a pas de possibilités pour MT.
Ici, toutes sortes de piscines sont vraiment inutiles. Les solutions standard de multithreading, si elles sont appliquées correctement, sont suffisantes.
C'est ce que je veux dire.
 

Les gars, je partage mes recherches.

J'ai écrit ma propre piscine de fil sur mes genoux. Je dois noter que c'est une version assez fonctionnelle, vous pouvez passer n'importe quel foncteur avec n'importe quels paramètres, en réponse le futur est retourné, c'est-à-dire que toutes les plushkas sous forme de capture d'exception et d'attente de terminaison sont disponibles. Et c'est aussi bon quehttps://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";
}

Je ne sais pas qui et dans quel état a écrit std::async, mais mon truc développé à genoux est 4 fois plus rapide que le standard (avec 10 threads de travail). Augmenter le nombre de threads par rapport au nombre de cœurs ne fait que me ralentir. Avec une taille de pool == nombre de cœurs (2), l'asynchrone perd environ 30 fois. Alors c'est comme ça.

Si je veux mettre des threads en commun, ce ne sera pas de l'asynchrone standard, c'est sûr...).

 
Vict:

Les gars, je partage mes recherches.

J'ai écrit ma propre piscine de fil sur mes genoux. Je dois noter que c'est une version assez fonctionnelle, vous pouvez passer n'importe quel foncteur avec n'importe quels paramètres, en réponse le futur est retourné, c'est-à-dire que toutes les plushkas sous forme de capture d'exception et d'attente de terminaison sont disponibles. Et je l'ai utilisé aussi bien que là https://www.mql5.com/ru/forum/318593/page34#comment_12700601.

Je ne sais pas qui et dans quel état a écrit std::async, mais mon truc développé à genoux est 4 fois plus rapide que le standard (avec 10 threads de travail). Augmenter le nombre de threads par rapport au nombre de cœurs ne fait que me ralentir. Avec une taille de pool == nombre de cœurs (2), l'asynchrone perd environ 30 fois. Alors c'est comme ça.

Si je veux mettre des threads en commun, ce ne sera pas de l'asynchrone standard, c'est sûr...).

Merci pour cette recherche. C'est un bon exemple, quelque chose auquel il faut réfléchir et dont il faut s'inspirer.
Mais d'après notre discussion générale, la plupart d'entre nous sont arrivés à la conclusion qu'un bassin d'écoulement n'est pas vraiment nécessaire.
Dans mon cas, c'est sûr, depuis que j'ai réalisé que le pool est statique en termes de nombre de threads, cela ne fonctionne pas pour moi.
Mais oui, quand j'aurai besoin d'une piscine, votre exemple sera parfait. Merci de montrer des exemples.
J'ai encore du mal à m'y retrouver ;))


Raison: