drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.04k stars 1.06k forks source link

同样的程序运行于多核机器和单核机器,一个正常一个崩溃? #2078

Open bethebest0622 opened 1 week ago

bethebest0622 commented 1 week ago

请看如下代码

#include <drogon/drogon.h>

auto cclient_ = drogon::HttpClient::newHttpClient("https://fapi.binance.com");

int main() {
  std::thread t([] () {
    drogon::app().disableSigtermHandling();
    drogon::app().run();
  }); 
  t.detach();

  while (true) {
    auto req = drogon::HttpRequest::newHttpRequest();
    req->setMethod(drogon::HttpMethod::Get);
    req->setContentTypeString("application/json");
    const auto & [req_result, reponse_ptr] = cclient_->sendRequest(req);
    sleep(20);
  }
}

这段代码多核机器正常,单核机器报错:

a.out: /usr/local/include/drogon/HttpClient.h:132: std::pair<drogon::ReqResult, std::shared_ptr<drogon::HttpResponse> > drogon::HttpClient::sendRequest(const drogon::HttpRequestPtr&, double): Assertion `!getLoop()->isInLoopThread() && "Deadlock detected! Calling a sync API from the same loop as " "the HTTP client processes on will deadlock the event loop"' failed.
[1]    1506059 abort      ./a.out

为啥单核会遇到死锁呢?按我的理解这段代码只有一个线程

nqf commented 1 week ago

不要定义成全局 cclient_

#include <drogon/drogon.h>

int main() {
    std::thread t([]() {
        drogon::app().disableSigtermHandling();
        drogon::app().run();
    });
    t.detach();
    sleep(1);
    auto cclient_ = drogon::HttpClient::newHttpClient("https://fapi.binance.com");
    while (true) {
        auto req = drogon::HttpRequest::newHttpRequest();
        req->setMethod(drogon::HttpMethod::Get);
        req->setContentTypeString("application/json");
        const auto& [req_result, reponse_ptr] = cclient_->sendRequest(req);
        sleep(20);
    }
}
bethebest0622 commented 1 week ago

不要定义成全局 cclient_

#include <drogon/drogon.h>

int main() {
  std::thread t([]() {
      drogon::app().disableSigtermHandling();
      drogon::app().run();
  });
  t.detach();
  sleep(1);
  auto cclient_ = drogon::HttpClient::newHttpClient("https://fapi.binance.com");
  while (true) {
      auto req = drogon::HttpRequest::newHttpRequest();
      req->setMethod(drogon::HttpMethod::Get);
      req->setContentTypeString("application/json");
      const auto& [req_result, reponse_ptr] = cclient_->sendRequest(req);
      sleep(20);
  }
}

这个原理可以简单讲讲吗

nqf commented 1 week ago

the HTTP client processes on will deadlock the event loop

你可以定义成全局, 但是你要保证 drogon::app().run(); 那个线程先被执行, 至于原理你看看 run 那个函数, 创建 drogon::HttpClient::newHttpClient对象时候, 如果你不传递Loop, 它就会用这个 Loop 单例 , 这个 loop 单例 默认 main 函数线程中的, 因此 你发送时候 触发断言 失败了, 但是 如果run 线程 先被执行以后, 他就被moveToCurrentThread, 所以就可以工作了

void HttpAppFrameworkImpl::run()
{
    if (!getLoop()->isInLoopThread())
    {
        getLoop()->moveToCurrentThread();
    }

/// Make sure that the main event loop is initialized in the main thread.
drogon::InitBeforeMainFunction drogon::HttpAppFrameworkImpl::initFirst_([]() {
    HttpAppFrameworkImpl::instance().getLoop()->runInLoop(f);
});