【问题标题】:Cancelling a thread using pthread_cancel : good practice or bad使用 pthread_cancel 取消线程:好的做法或坏的做法
【发布时间】:2016-12-31 05:38:45
【问题描述】:

我在 Linux (CentOS 5.3) 上有一个 C++ 程序,它生成多个线程,这些线程处于无限循环中以执行工作并休眠几分钟。 现在我必须取消正在运行的线程,以防出现新的配置通知并重新启动新的线程集,为此我使用了 pthread_cancel。 我观察到的是,即使在收到取消指示后,线程也没有停止,甚至在睡眠完成后还有一些正在休眠的线程。

由于不希望出现这种行为,因此在上述场景中使用 pthread_cancel 会引发关于是好还是坏做法的问题。

请评论上述场景中的 pthread_cancel 用法。

【问题讨论】:

    标签: c++ linux pthreads


    【解决方案1】:

    一般来说线程取消并不是一个好主意。只要有可能,最好有一个共享标志,线程使用它来跳出循环。这样,您将让线程在实际退出之前执行它们可能需要执行的任何清理操作。

    关于线程实际上没有取消的问题,POSIX 规范确定了一组取消点 (man 7 pthreads)。只能在这些点取消线程。如果您的无限循环不包含取消点,您可以通过调用pthread_testcancel 添加一个。如果 pthread_cancel 已被调用,则此时将对其进行操作。

    【讨论】:

    • +1 用于避免取消,这是解决内存泄漏的最快方法,甚至更糟。而是礼貌地问:)
    • 似乎我必须使用共享标志更改线程终止的逻辑。但另一方面,我的程序的取消状态设置为 ASYNCHRONOUS 的线程,我相信这与调用相应的清理处理程序后立即终止线程有关。
    • @user584631:pthreads 手册页说,当模式设置为异步时,可以立即取消,但系统不需要这样做。
    • 系统不需要立即取消线程意味着直到那时线程执行仍然不知道取消,是这样吗?
    • 不能同意。 pthread_cancel() 可能更容易使用。如果您调用pthread_cleanup_push()pthread_cleanup_pop() 来设置清理处理程序,则可以避免资源泄漏。
    【解决方案2】:

    如果您正在编写异常安全的 C++ 代码(请参阅 http://www.boost.org/community/exception_safety.html),那么您的代码自然就可以取消线程了。 glibs throws C++ exception on thread cancel,以便您的析构函数可以进行适当的清理。

    【讨论】:

    • 如果 OP 知道他们永远不需要在另一个 pthreads 实现上运行,那没关系,但我建议不要依赖通过异常实现线程取消。使用标志将线程退出从“隐藏的东西”领域移到您可以看到的代码中,从而使以后的维护者更容易。多年来,我一直在处理这个特定问题,我得出的结论是,最好的办法是避免取消,因为当你在混合中取消时,总会有一些事情让你绊倒。
    • 这篇博文描述了为什么 RAII 不能与取消组合:skaark.wordpress.com/2010/08/26/…
    • @erenon 不错的文章,但这只是故事的一半。可以在析构函数中禁用线程取消。
    • @MaximEgorushkin 对一个 5 年前的问题的评论很晚,但我喜欢你的想法。我可以用 gcc 指定所有 C++ 析构函数应该自动(暂时)禁用 pthread_cancel 吗?
    • @ErikAlapää 可能不会。
    【解决方案3】:

    您可以执行以下代码的等效操作。

    #include <pthread.h>
    #include <cxxabi.h>
    #include <unistd.h>
    ...
    void *Control(void* pparam)
    {
        try
        {
            // do your work here, maybe long loop
        }   
        catch (abi::__forced_unwind&)
        {  // handle pthread_cancel stack unwinding exception
            throw;
        }
        catch (exception &ex) 
        {
            throw ex;
        }
    }
    
    int main()
    {
        pthread_t tid;
        int rtn;
        rtn = pthread_create( &tid, NULL, Control, NULL );
    
        usleep(500);
        // some other work here
    
        rtn = pthtead_cancel( tid );
    }
    

    【讨论】:

      【解决方案4】:

      我会使用 boost::asio。

      类似:

      struct Wait {
        Wait() : timer_(io_service_), run_(true) {}
      
        boost::asio::io_service io_service_;
        mutable boost::asio::deadline_timer timer_;
        bool run_;
      };
      
      void Wait::doWwork() {
        while (run) {
          boost::system::error_code ec;
          timer_.wait(ec);
          io_service_.run();
          if (ec) {
            if (ec == boost::asio::error::operation_aborted) {
              // cleanup
            } else {
              // Something else, possibly nasty, happened
            }
          }
        }
      }
      
      void Wait::halt() {
        run_ = false;
        timer_.cancel();
      }
      

      一旦你熟悉了它,asio 就是一个很棒的工具。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 2016-11-10
        • 1970-01-01
        • 1970-01-01
        • 2010-11-02
        • 1970-01-01
        • 2020-05-19
        相关资源
        最近更新 更多