MQL中的异步和多线程编程 - 页 36

 
Vict:

我想到的唯一合理的解释是手动控制池中运行的线程数量(如果我们不相信默认的async|deferred)--例如,如果我们看到系统负载很重,就重新发送带有async标志的作业,延迟发送。

总的来说,我对async()的迟钝有些失望,我将创建自己的轻量级线程池,在我看来,它将快得多。

书中描述了默认的async|deferred模式,因此如果没有足够的物理资源,任务不会创建新的线程,而是在主线程中执行,将其阻塞。
而且你应该时刻注意默认模式下主线程可能被锁定的情况,因为大多数时候你需要的解决方案是不要阻塞主线程。
也就是说,默认模式会根据物理资源(即处理器)的负载自动切换到哪里执行任务。
出于这个原因,如果我们确信物理资源不会被超载,我们最好明确指定标志std::launch::async。
但如果你溢出了现代处理器的物理资源,你将不得不寻找这样的计算,以发挥其全部潜力 ))
关于速度我不能说什么,因为我还在研究这个理论 ))

 
Roman:

所以,如果我们确定物理资源不会被溢出,最好是明确指定标志std::launch::async。
而要溢出现代处理器的物理资源,需要很长的时间来找到这样的计算方法来选择所有潜在的))。

处理器可以承受更多的线程,但操作系统的能力更有可能成为瓶颈。好吧,它不能无休止地繁殖线程,迟早async(launch::async, ...)会遇到一个被抛出的异常。

 
Vict:

处理器甚至可以承受大量的线程,但操作系统的能力更有可能成为瓶颈。好吧,它不能无休止地繁殖线程,迟早async(launch::async, ...)会遇到一个被抛出的异常。

是的,总是有一个物理限制,但在我们的任务中,不太可能超过这个限制,为mt5。
另外,async和future在它们的返回值中,如果出现异常,无论我们如何获得这个值,通过lambda函数、ref()或.get()都会返回。
而std::thread在其返回值中不能返回异常。

 
Roman:

是的,总是有一个物理限制,但在我们对mt5的任务中,不太可能超过这个限制。
另外,async和future在它们的返回值中,如果出现异常,无论我们如何获得这个值,通过lamda函数、ref()或.get()都会返回。
而std::thread在其返回值中不能返回异常。

我认为你不应该对async感到太兴奋。这似乎是为了方便而做的,但所有这些东西似乎真的击中了性能。这不是由一个加一。

而返回值中的std::thread不能返回异常。

这并不总是必要的。但如果你这样做,就会多出十几行(尽管它的工作速度会更快--没有所有的分配在堆里)。
 
Vict:
但如果你不得不这样做,那就是多了十几行(虽然它的工作速度会更快--没有堆积的所有分配)。

以便不至于没有事实根据。

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

甚至不需要一打。是的,你可以做更多的低级别的事情,而不需要所有的packaged_task和future的东西,但这个想法是,抛出异常并不是一些超级异步的事情,线程绝对没有什么。

 
也许,从实用的角度来看,有时值得远离所有这些垫子,记住Windows的api--CreateThread、同步原语、互锁函数。同样,也有。当然,当你为风写的时候。为什么要把事情复杂化,因为MT4|MT5没有这么复杂的任务,需要延迟计算,集合等等。
 
Andrei Novichkov:
既然MT4|MT5没有复杂的任务需要延迟计算、资金池等,为什么还要把事情复杂化?
实际上是有任务的。MT不具备这种能力。
在这里,各种各样的池子真的没有必要。标准的多线程解决方案,如果应用正确,就足够了。
 
Yuriy Asaulenko:
实际上,挑战是存在的。没有MT的可能性。
在这里,各种各样的池子真的没有必要。标准的多线程解决方案,如果应用正确,就足够了。
这就是我的意思。
 

伙计们,我在分享我的研究。

我在膝盖上写了自己的线池。我应该注意到,这是一个相当实用的版本,你可以传递任何带有任何参数的函数,作为对未来的回应,也就是说,所有以捕获异常和等待终止为形式的plushkas都是可用的。而且它和https://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";
}

我不知道是谁在什么状态下写了std::async,但我膝盖上开发的东西比标准的快4倍(有10个工作线程)。增加线程数超过内核数只会使我的速度变慢。在池子大小==核心数(2)的情况下,async输了大约30倍。所以,事情就是这样。

如果我想建立线程池,肯定不会是标准的异步))。

 
Vict:

伙计们,我在分享我的研究。

我在膝盖上写了自己的线池。我应该注意到,这是一个相当实用的版本,你可以传递任何带有任何参数的函数,作为对未来的回应,也就是说,所有以捕获异常和等待终止为形式的plushkas都是可用的。而我用的也是那里https://www.mql5.com/ru/forum/318593/page34#comment_12700601

我不知道是谁在什么状态下写了std::async,但我膝盖上开发的东西比标准的快4倍(有10个工作线程)。增加线程数超过内核数只会使我的速度变慢。在池子大小==核心数(2)的情况下,async输了大约30倍。所以,事情就是这样。

如果我想建立线程池,肯定不会是标准的异步))。

谢谢你的研究。这是一个很好的例子,是值得思考和学习的地方。
但从我们的一般性讨论中,我们大多数人得出的结论是,流水池并不是真的需要。
在我的情况下,这是肯定的,因为我已经意识到池子里的线程数量是静态的,所以它对我不起作用。
但是,是的,当我需要一个游泳池时,你的例子将是恰到好处的。谢谢你展示的例子。
我仍然在掌握它的窍门 ))