我写了一个小的示例代码,展示了我正在编写的程序存在的相同问题:在调试模式下运行良好,在发布模式下会出现段错误。问题似乎是在发布模式下 ui_context 被调用执行它分配的工作时为 nullptr。
在 Fedora 33 上运行,使用 g++ (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9)
和 clang version 11.0.0 (Fedora 11.0.0-2.fc33)
这两个编译器的行为都相同。Boost 版本是 1.75。
代码:
#include <iostream>
#include <vector>
#include <memory>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
constexpr auto MAX_LOOP_COUNT = 100;
class network_client : public std::enable_shared_from_this<network_client>
{
private:
using Signal = boost::signals2::signal<void(int)>;
public:
network_client(boost::asio::io_context &context) :
strand(boost::asio::make_strand(context))
{
std::cout << "network client created" << std::endl;
}
void doNetworkWork()
{
std::cout << "doing network work" << std::endl;
boost::asio::post(strand,std::bind(&network_client::onWorkComplete,shared_from_this()));
}
void onWorkComplete()
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "signalling completion" << " from thread id:" << std::this_thread::get_id() << std::endl;
signal(42);
}
void workCompleteHandler(const typename Signal::slot_type &slot)
{
signal.connect(slot);
}
private :
boost::asio::strand<boost::asio::io_context::executor_type> strand;
Signal signal;
};
class network_client_producer
{
public :
network_client_producer() : work(boost::asio::make_work_guard(context))
{
using run_function = boost::asio::io_context::count_type (boost::asio::io_context::*)();
for (int i = 0; i < 2; i++)
{
context_threads.emplace_back(std::bind(static_cast<run_function>(&boost::asio::io_context::run), std::ref(context)));
}
}
~network_client_producer()
{
context.stop();
for(auto&& thread : context_threads)
{
if(thread.joinable())
{
thread.join();
}
}
}
using NetworkClientPtr = std::shared_ptr<network_client>;
NetworkClientPtr makeNetworkClient()
{
return std::make_shared<network_client>(context);
}
private :
boost::asio::io_context context;
std::vector<std::thread> context_threads;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
};
class desktop : public std::enable_shared_from_this<desktop>
{
public:
desktop(const boost::asio::io_context::executor_type &executor):executor(executor)
{
}
void doSomeNetworkWork()
{
auto client = client_producer.makeNetworkClient();
client->workCompleteHandler([self = shared_from_this()](int i){
//post work into the UI thread
std::cout << "calling into the uiThreadWork with index " << i << " from thread id:" << std::this_thread::get_id() << std::endl;
boost::asio::post(self->executor, std::bind(&desktop::uiThreadWorkComplete, self, i));
});
client->doNetworkWork();
}
void showDesktop()
{
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
public:
void uiThreadWorkComplete(int i)
{
std::cout << "Called in the UI thread with index:" << i << ", on thread id:" << std::this_thread::get_id() << std::endl;
}
private:
const boost::asio::io_context::executor_type& executor;
network_client_producer client_producer;
};
int main()
{
std::cout << "Starting application. Main thread id:"<<std::this_thread::get_id() << std::endl;
int count = 0;
boost::asio::io_context ui_context;
auto work = boost::asio::make_work_guard(ui_context);
/*auto work = boost::asio::require(ui_context.get_executor(),
boost::asio::execution::outstanding_work.tracked);*/
auto ui_desktop = std::make_shared<desktop>(ui_context.get_executor());
ui_desktop->doSomeNetworkWork();
while(true)
{
ui_context.poll_one();
ui_desktop->showDesktop();
if (count >= MAX_LOOP_COUNT)
break;
count++;
}
ui_context.stop();
std::cout << "Stopping application" << std::endl;
return 0;
}
使用 g++ -std=c++17 -g -o main -pthread -O3 main.cpp
编译并在 gdb 中运行,我得到了以下结果:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Starting application. Main thread id:140737348183872
[New Thread 0x7ffff7a51640 (LWP 27082)]
[New Thread 0x7ffff7250640 (LWP 27083)]
network client created
doing network work
signalling completion from thread id:140737348179520
calling into the uiThreadWork with index 42 from thread id:140737348179520
Thread 2 "main" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7a51640 (LWP 27082)]
0x000000000040b7b8 in boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u>::execute<std::_Bind<void (desktop::*(std::shared_ptr<desktop>, int))(int)> >(std::_Bind<void (desktop::*(std::shared_ptr<desktop>, int))(int)>&&) const (this=<optimized out>, f=...) at /usr/local/include/boost/asio/impl/io_context.hpp:309
309 io_context_->impl_.post_immediate_completion(p.p,
在没有进行任何优化的情况下编译,g++ -std=c++17 -g -o main -pthread -O0 main.cpp
的工作效果符合预期。
我尽可能地让它接近实际执行网络IO操作的程序,这也是为什么我把那个strand放进去的原因。
很明显我在这里做错了什么。问题是:出了什么问题? 感谢您提供的任何指针。