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

时间:2017-05-18 11:35:18

标签: c++ multithreading

我编写了这段代码来测试boost 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时,程序正常退出,有时不退出(控制台挂起)。那是为什么?

我在这里做错了什么?

2 个答案:

答案 0 :(得分:1)

代码中的主要问题是printer即使在销毁之后也会被调用:线程被分离,因此即使主函数结束且printer为<,它们也可能正在运行强>破坏即可。有了这个问题,就不可能有一个已定义的行为,因为线程仍然可以使用被销毁的printer。悬挂有时不会发生 - 未定义的行为。为什么这个问题很难具体说明。这里显而易见的是线程正在使用垃圾数据。

总结缺陷:

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

  2. 即使在销毁之后,io_service实例也有可能被使用:线程的labmdas持有引用,run方法可能仍处于执行过程中对象被销毁(对静态变量破坏和分离的线程终结的相对顺序没有任何保证,以及boost::asio::io_service不会阻止析构函数完成run方法。 / p>

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

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

答案 1 :(得分:-1)

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

你应该尝试强制它们在主程序退出之前运行,或者通过使它们可以连接,或者如果你真的想通过在程序退出之前添加一个睡眠来尝试它们来分离它们。