如何从外部库

时间:2015-09-03 14:11:47

标签: c++ multithreading qt

我正在使用外部库来查找最少的功能(优化)。有时它需要很多时间,我想停止计算(例如通过点击按钮)。所以我在另一个线程中执行优化。我见过的所有例子都是这样的(在QThread的子类中):

void run(){
    for(int i=0;i<100000;i++){
        if(!flag) break;
        //do something
     }
}

当主要线程将标志设置为false时,第二个线程停止。问题是我不能把这样的标志放到外部库中。但是我提供了对mininize的函数的引用,以便每次迭代都会调用该函数。所以我把旗帜放进了这个功能:

double function(double x){
    if(!flag)  ;//here I should kill thread
    return x*x*x+2*x*x+7*x+7;
}

那么如何杀死那个帖子?

4 个答案:

答案 0 :(得分:1)

QThread有一个函数terminate(),可用于取消线程。这可能是一个坏主意,因为这意味着线程创建的具有自动存储持续时间的对象将不会被清除。

您可以尝试抛出异常,并在线程的顶层函数中捕获它。但是,当然,控制传递的外部库必须是异常安全的才能工作。

如果做不到这一点,可能的解决方案会变得更加模糊:您可以尝试从函数中返回特殊值,希望外部库能够干净地中止(或优雅地结束)迭代。此类值的候选人为0.0-std::numeric_limits<double>::infinity()std::numeric_limits<double>::quiet_NaN()

答案 1 :(得分:1)

有两个问题:

  1. 如果在线程内部运行的代码已经分配了任何资源(内存,文件句柄......),那么如果你QThread::terminate工作线程,它们就会泄漏。

  2. 目前尚不清楚优化库是否有设置方法告诉它要终止迭代。如果库在您的控制之下,您只需让它在从函数中获得NaN后停止迭代。

  3. 您正在寻找的旗帜是QThread::isInterruptionRequested。要设置它,请在线程上调用requestInterruption()。有许多可能的方法可以强制您的库终止迭代。您可以尝试以下方法:

    • 从函数中返回std::numeric_limits<double>::quiet_NaN()

      double function(double x){
        return isInterruptionRequested()
          ? std::numeric_limits<double>::quiet_NaN() 
          : x*x*x+2*x*x+7*x+7;
      }
      
    • 从函数中返回一个常量。

      double function(double x){
        return isInterruptionRequested()
          ? 0.0 : x*x*x+2*x*x+7*x+7;
      }
      
    • 将函数切换为最近值附近的全局最小值:

      class MyThread : public QThread {
        bool m_wasInterrupted;
        double m_minimum;
        double function(double x){
          if (!isInterruptionRequested())
            return x*x*x+2*x*x+7*x+7;
          if (!m_wasInterrupted) {
            m_minimum = x;
            m_wasInterrupted = true;
          }
          return abs(x-m_minimum) : 
        }
        void run() {
          m_wasInterrupted = false;
          ...
        }
      };
      
    • 强制例外:

      class end_iteration_exception : public std::exception {};
      
      class MyThread : public QThread {
        ...
        double function(double x){
          if (isInterruptionRequested()) throw end_iteration_exception();
          return x*x*x+2*x*x+7*x+7;
        }
        void run() {
          try {
            ...
          } catch (const end_iteration_exception&) {
            // we've been interrupted
          }
        }
      };
      

    如果使用C ++编写,优化库应该使用仿函数,因此您可以从std::bind(&MyThread::function, this)内传递run()

    如果它没有,而是有一个只带有函数指针的C api,那么它应该提供一个版本,允许你将一个常量参数作为第二个参数传递给函数:

    double findMinimum(double (*fun)(double, void*), void * param) {
      ...
      double val = fun(x, param);
      ...
      return x;
    }
    

    然后,您的函数会将第二个参数表示为线程实例:

    class MyThread {
      static double function(double x, void* p){
        auto self = reinterpret_cast<MyThread*>(p);
        return self->isInterruptionRequested()
          ? 0.0 : x*x*x+2*x*x+7*x+7;
      }
      void run() {
        ...
        auto x = findMinimum(&function, this);
        ...
      }
      ...
    };
    

    如果它甚至不这样做,那么你就不能诉诸全球状态:

    class MyThread;
    namespace {
    MyThread * threadState { nullptr };
    }
    
    class MyThread : public QThread {
      ...
      void run() {
        threadState = this;
        ...
        threadState = nullptr;
      }
      static double function(double x) {
        return threadState->isInterruptionRequested() ? ...;
      }
    };
    

答案 2 :(得分:0)

不幸的是,你无能为力。如果您不想杀死/终止该线程 - 这很好,您最多可以放弃这样的线程并允许它结束。如果您的功能不允许取消其工作,则情况属实。

您也可以尝试在较小的多个批次中进行工作,但这也取决于您的功能如何工作。

可能有一些特定于平台的技术可能会有所帮助。例如,异常注入,您可能会在本文http://www.codeproject.com/Articles/71529/Exception-Injection-Throwing-an-Exception-in-Other中读到,在Windows下应该可以为线程注入异常,这样就可以优雅地(使用调用析构函数)完成外部库函数。< / p>

答案 3 :(得分:0)

在Unix上杀死线程的一种方法是设置此线程的信号处理并发送信号。线程会看到一个信号并可以退出。但是,这不是可扩展的,因为每个信号只能有一个处理,这意味着您运行的每个线程都必须响应特定的信号。