为什么线程分离时程序挂起



我写了这段代码来测试提升asio与分离线程的一些行为。

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
#include <string>
#include <thread>
class printer {
public:
printer(boost::asio::io_service &io)
: timer_(io, boost::posix_time::seconds(1)), count_(0) {
timer_.async_wait(boost::bind(&printer::print, this));
}
~printer() { std::cout << "Final count is " << count_ << "n"; }
void print() {
if (count_ < 10) {
std::cout << "thread " << std::this_thread::get_id()
<< ", count = " << count_ << std::endl;
++count_;
timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
timer_.async_wait(boost::bind(&printer::print, this));
}
}
private:
boost::asio::deadline_timer timer_;
int count_;
};
boost::asio::io_service io;
int main() {
boost::asio::io_service::work work(io);
std::cout << "main thread " << std::this_thread::get_id() << std::endl;
std::thread t([] { io.run(); });
std::thread t2([] { io.run(); });
t.detach();
t2.detach();
printer p(io);
std::string name;
std::cout << "Press a key";
std::getline(std::cin, name);
std::cout << "finished" << std::endl;
return 0;
}

我想看看当我有两个工作线程运行 io_service.run 方法时会发生什么,以及当它们分离时会发生什么(尤其是程序退出时会发生什么)。

第一个问题是,当我在linux上运行这个程序时,我在打印机中只能看到一个线程ID。不知何故,第二个线程不会从io_service中获取任务,即使它应该这样做,因为它正在运行 io_service.run 方法。

我看到的第二个问题是,有时当我在打印机的所有 10 个打印输出完成之前按 ENTER 时,程序会正常退出,有时不会(控制台挂起)。为什么?

我在这里做错了什么?

代码中的主要问题是,即使在printer销毁后,它们也会被调用:线程是分离的,因此即使主函数结束并且printer销毁,它们也可能正在运行。对于此问题,不可能定义行为,因为线程仍可能与被破坏的printer一起使用。挂起有时会发生有时不会 - 未定义的行为。为什么会发生这种情况很难具体说。这里显而易见的是线程正在使用垃圾数据。

总结缺陷:

  1. 即使在销毁后,printer实例也有可能被使用;

  2. 即使在销毁后,io_service实例也有可能被使用:线程的 labmda 保存引用,并且run方法可能仍在执行过程中,而对象被销毁(对于静态变量销毁和分离线程终止的相对顺序没有任何保证, 以及boost::asio::io_service不会阻止析构函数来完成run方法)。

我的建议是引入一个明确的破坏顺序。不幸的是,你不能只是说:好吧,我完成了,线程分离了,我退出了。因为线程中仍有工作正在进行,但相关对象被销毁。

class printer {
public:
printer(boost::asio::io_service& io)
: timer_(io, boost::posix_time::seconds(1)), count_(0) {
timer_.async_wait(
boost::bind(&printer::print, this));
}
~printer() { std::cout << "Final count is " << count_ << "n"; }
void print() {
if (count_ < 10) {
std::cout << "thread " << std::this_thread::get_id() << ", count = " << count_
<< std::endl;
++count_;
timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
timer_.async_wait(
boost::bind(&printer::print, this));
}
}
boost::asio::deadline_timer timer_;
int count_;
};
boost::asio::io_service io;
int main() {
auto work = std::unique_ptr<boost::asio::io_service::work>(
new boost::asio::io_service::work(io));
std::cout << "main thread " << std::this_thread::get_id() << std::endl;
std::thread t([&] { io.run(); });
std::thread t2([&] { io.run(); });
printer p(io);
std::string name;
std::cout << "Press a key";
std::getline(std::cin, name);
work.reset();
io.stop();
t.join();
t2.join();
std::cout << "finished" << std::endl;
return 0;
}

程序的结果取决于两个分离线程的执行顺序。有时它们可能在主程序完成后开始运行,因此io 对象已经被销毁。

您应该尝试强制它们在主程序退出之前运行,方法是使它们可加入,或者,如果您真的想将它们作为分离尝试,请在程序退出之前添加睡眠。

最新更新