【问题标题】:How to kill thread which is calculating function from external library如何杀死正在从外部库计算函数的线程
【发布时间】:2015-09-03 14:11:47
【问题描述】:

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

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

当主线程将标志设置为false 时,第二个线程停止。问题是我不能将这样的标志放入外部库中。但是,我提供了对要最小化的函数的引用,以便每次迭代都会调用该函数。所以我在那个函数中加入了标志:

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

那么如何杀死那个线程呢?

【问题讨论】:

标签: c++ multithreading qt


【解决方案1】:

有两个问题:

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

  2. 尚不清楚您的优化库是否有一套方法来告诉它要终止迭代。如果库在您的控制之下,您应该简单地让它在从函数中获取NaN 时停止迭代。

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

  • 从函数返回一个std::numeric_limits&lt;double&gt;::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++ 编写的优化库,则应该采用函子,因此您可以从 run() 中将其传递给 std::bind(&amp;MyThread::function, this)

如果没有,而是有一个只接受函数指针的 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() ? ...;
  }
};

【讨论】:

  • 我已经简化了我的问题以使示例更清晰。实际上它是多维优化,函数中的参数是一个向量。我还提供了计算梯度向量和粗麻布矩阵的函数(给库)。所以现在当标志打开而不是计算梯度时,我只返回零向量。它有效!
  • @lnk 太好了。我很高兴这样一个简单的解决方法奏效了。
【解决方案2】:

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

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

如果做不到这一点,可能的解决方案会变得更加棘手:您可以尝试从函数中返回特殊值,希望外部库能够干净地中止(或优雅地结束)迭代。这些值的候选者是0.0-std::numeric_limits&lt;double&gt;::infinity()std::numeric_limits&lt;double&gt;::quiet_NaN()

【讨论】:

  • @KubaOber QThread::terminate 不被推荐,并且是非常糟糕的做法,因为它在随机位置终止线程,没有资源被释放。
  • @marcinj 当然资源会泄漏,但你还能做什么?真的没什么。如果计算是在一个可以终止而不用担心资源泄漏的子进程中完成的,那可能会更容易。
  • @KubaOber 谢谢,注意到了。不知道为什么我第一次查看文档时忽略了这一点。
【解决方案3】:

不幸的是,您无能为力。如果您不想杀死/终止该线程 - 这很好,您最多可以放弃该线程并让它结束。如果您的函数不允许取消其工作,则这是正确的。

您也可以尝试以较小的多个批次进行工作,但这也取决于您的函数的工作方式。

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-23
      • 2011-01-06
      • 1970-01-01
      相关资源
      最近更新 更多