Programação assíncrona e multi-tarefa em MQL - página 36

 
Vict:

A única explicação razoável que me vem à mente é o controle manual sobre o número de fios que correm na piscina (se não confiarmos na assimetria padrão) - por exemplo, se virmos que o sistema está muito carregado, reenviamos os trabalhos com a bandeira assimétrica, os enviamos adiados.

Em geral, estou um pouco desapontado com a lentidão da async(), vou criar meu próprio pool de fios leves, me parece que será muito mais rápido.

O modo assimétrico padrão é descrito no livro para que, se não houver recursos físicos suficientes, a tarefa não crie um novo fio, mas seja executada no fio principal, bloqueando-o.
E você deve estar sempre ciente de um possível bloqueio da rosca principal no modo padrão, porque na maioria das vezes você precisa de soluções para não bloquear a rosca principal.
Ou seja, o modo padrão muda automaticamente onde executar a tarefa dependendo da carga de um recurso físico, ou seja, CPU.
Por este motivo, se tivermos certeza de que o recurso físico não será sobrecarregado, é melhor especificar explicitamente a bandeira std::lançamento::async.
Mas se você transbordar um recurso físico de processadores modernos, você terá que procurar por tais cálculos para atingir seu potencial máximo ))
Eu não posso dizer nada sobre velocidade, porque ainda estou estudando a teoria ))

 
Roman:

Portanto, se tivermos certeza de que o recurso físico não será transbordado, é melhor especificar a bandeira explicitamente std::lançamento::async.
E para transbordar um recurso físico de processadores modernos, leva muito tempo para encontrar tais cálculos para selecionar todo o potencial ))

O processador pode suportar um número ainda grande de roscas, mas as capacidades do sistema operacional são mais propensas a se tornar um gargalo. Bem, não pode multiplicar infinitamente os fios, mais cedo ou mais tarde, a assimetria (lauch::async, ...) se deparará com uma exceção.

 
Vict:

O processador pode suportar até mesmo um grande número de roscas, mas é mais provável que as capacidades do sistema operacional se tornem um gargalo. Bem, não pode multiplicar infinitamente os fios, mais cedo ou mais tarde, a assimetria (lauch::async, ...) se deparará com uma exceção.

Sim, há sempre um limite físico, mas é pouco provável que este limite seja excedido em nossas tarefas para o mt5.
Também assimétricos e futuros em suas exceções de retorno de valor, se ocorrerem, não importa como obtemos este valor, através de uma função lambda, ref() ou .get().
E std::thread em seu valor de retorno não pode retornar exceções.

 
Roman:

Sim, há sempre um limite físico, mas em nossas tarefas para o mt5, é pouco provável que ele ultrapasse este limite.
Também assimétricos e futuros em suas exceções de retorno de valor, se ocorrerem, não importa como obtemos este valor, através de uma função lamda, ref() ou .get().
E std::thread em seu valor de retorno não pode retornar exceções.

Acho que você não deve ficar muito entusiasmado com as assimetrias. Parece ter sido feito por conveniência, mas todas essas coisas parecem realmente atingir o desempenho. Não é por um mais um.

E std:::linha no valor de retorno não pode retornar exceções.

Nem sempre é necessário. Mas se o fizer, é uma dúzia de linhas extras (mesmo que funcione mais rápido - sem toda a alocação na pilha).
 
Vict:
Mas se for preciso, é uma dúzia de linhas extras (enquanto funcionaria mais rápido - sem toda a alocação na pilha).

Para não ser insubstanciado:

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

Não precisava nem mesmo de uma dúzia. Sim, você pode fazer ainda mais coisas de baixo nível sem toda a tarefa_embalada e coisas futuras, mas a idéia é que lançar exceções não é uma coisa superassíncrona, e o fio não tem absolutamente nada.

 
Talvez, de um ponto de vista prático, às vezes valesse a pena se afastar de todas essas almofadas e lembrar da api Windows - CreateThread, sincronização de primitivos, funções interligadas. Mesmo assim, existem. É claro, quando se escreve para os ventos. Por que complicar as coisas quando o MT4|MT5 não tem tarefas tão complicadas que exijam cálculos atrasados, pools e assim por diante.
 
Andrei Novichkov:
Por que complicar as coisas quando não há tarefas complexas para o MT4|MT5 que exigem cálculos atrasados, pools, etc.?
Na verdade, há tarefas. A MT não tem as capacidades.
Aqui, todos os tipos de piscinas são realmente desnecessários. Soluções padrão multi-tarefas, se aplicadas corretamente, são suficientes.
 
Yuriy Asaulenko:
Na verdade, os desafios estão aí. Não há possibilidades para a MT.
Aqui, todos os tipos de piscinas são realmente desnecessários. Soluções multithreading padrão, se aplicadas corretamente, são suficientes.
É isso que eu quero dizer.
 

Rapazes, estou compartilhando minhas pesquisas.

Escrevi minha própria piscina de fios sobre meus joelhos. Devo notar que é uma versão bastante funcional, você pode passar qualquer funerador com qualquer parâmetro, em resposta futura é devolvido, ou seja, todos os plushkas na forma de captura de exceção e à espera de rescisão estão disponíveis. E é tão bom quantohttps://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";
}

Não sei quem e em que estado escreveu std::async, mas meu desenvolvimento do joelho é 4 vezes mais rápido que o padrão (com 10 fios de trabalho). O aumento do número de fios sobre o número de núcleos só me atrasa. Com o tamanho da piscina == número de núcleos (2), a assimetria perde em cerca de 30 vezes. Então é assim que é.

Se eu quiser juntar as roscas, não será com certeza uma assimetria padrão )).

 
Vict:

Rapazes, estou compartilhando minhas pesquisas.

Escrevi minha própria piscina de fios sobre meus joelhos. Devo notar que é uma versão bastante funcional, você pode passar qualquer funerador com qualquer parâmetro, ele retorna no futuro em resposta, ou seja, todos os plushkas na forma de captura de exceções e à espera de rescisão estão disponíveis. E eu o usei tão bem quanto lá https://www.mql5.com/ru/forum/318593/page34#comment_12700601.

Não sei quem e em que estado escreveu std::async, mas meu desenvolvimento do joelho é 4 vezes mais rápido que o padrão (com 10 fios de trabalho). O aumento do número de fios sobre o número de núcleos só me atrasa. Com o tamanho da piscina == número de núcleos (2), a assimetria perde em cerca de 30 vezes. Então é assim que é.

Se eu quiser juntar as roscas, não será com certeza uma assimetria padrão )).

Obrigado pela pesquisa. É um bom exemplo, algo para se pensar e aprender com ele.
Mas a partir de nossa discussão geral, a maioria de nós chegou à conclusão de que um pool de fluxos não é realmente necessário.
No meu caso, isso é certo, já que percebi que a piscina é estática em termos de número de fios, não funciona para mim.
Mas sim, quando eu precisar de uma piscina, seu exemplo virá a calhar. Obrigado por mostrar exemplos.
Eu ainda estou pegando o jeito ))