1

我需要一个超时函数调用。我主要使用 Qt(4.7.3、4.8.5),所以我试图用 Qt 找到解决方案。QFuture 和 QtConcurrent 这两个类似乎完成了我需要的 99%,但我找不到使函数调用超时的可能性。

我的问题: 我有我的测试仪(gtest)并且我测试了一个可以以无限循环结束的函数=>所以我想测试它来解决这个问题(这个函数是内部极端复杂的:-()。我如果存在无限循环,我想添加一个超时时间来中断 => 所以我可以告诉测试人员有什么问题(无限循环),我将终止线程。

我正在寻找类似的东西:

QtConcurrent::run(............,30000 /*30 seconds timeout)*/;

有人知道我该怎么做吗? (如果可以使用本机 C++ 或 boost,或者......你也可以告诉我你的解决方案)

4

4 回答 4

2

我专注于 QtConcurrent 模块,因为您在问题中提到了它。但是您也可以使用普通的 QThread 来实现您的目标:

// A Thread calling your test function
class MyThread : public QThread {
protected:
  void run() { myFunction(); }
};


// calling the function
MyThread t;
t.start();
if ( t.wait(30000) ) {
  // Finished
} else {
  // Not finished
  // ATTENTION: USE TERMINATE WITH CARE (see QThread documentation)!
  t.terminate();
}

终止调用将强制停止线程,从而停止函数的执行。但是请注意,线程无法清理,并且您的函数使用的任何资源都没有正确释放。


旧答案:
您可以使用 Qt 执行此操作,使用 a QFutureWatcher、 aQTimer和 helper QEventLoop。设置您的并发运行,并使用观察者观察结果。使用计时器设置超时,并在事件循环中等待任何一个完成。

// Setup eventloop, watcher and timer
QEventLoop loop;
QFutureWatcher watcher;
QObject::connect( &watcher, SIGNAL(finished()), &loop, SLOT(quit()));
QTimer::singleShot( 30000, &loop, SLOT(quit()) );

// Start function call
QFuture<T> future = QtConcurrent::run(...);

// Watch the call
watcher.setFuture( future );

// Wait until event loop finishes
loop.exec();

// Now either future has finished, or timeout was reached...
if ( future.isFinished() ) {
  // Function completed!
} else {
  future.cancel();
  // Infinite loop...
}
于 2013-11-11T10:24:37.573 回答
1

问题是如果没有线程的合作,您将无法安全地终止线程。线程可能正在访问一些共享数据结构,例如 C 运行时堆,如果它被强制终止,那么这些数据结构将保持在更改中间状态,基本上是损坏的。将来从程序内部对它们的任何访问都可能导致崩溃或更糟。

无限循环的情况与其他不良情况没有任何不同。如果你启动的函数使程序崩溃了,你如何向测试人员报告呢?

在您的情况下,您根本不信任您正在执行的代码。解决方案是在单独的进程中启动它,操作系统保证其安全终止。通过将测试代码与测试应用程序隔离开来,您保证可以检测到所有类型的问题,包括超时或异常终止。

于 2015-11-16T12:33:28.173 回答
-2

我认为您需要找出为什么会发生无限循环,而不是试图解决症状。

QtConcurrent::run()使用线程池来运行任务。您不能安全地终止线程,因此安全地完成任务的唯一方法是return从它开始。

如果您仍然想要超时,您可以使用循环向您的函数添加类似的内容:

QTime t;
t.start();

// it's your loop
while( true ) {

    if( t.elapsed() > 30*1000 ) {
        return 1;
    }
    ...
}
于 2013-11-11T10:12:01.163 回答
-2

您可以将 QTimer 信号绑定到插槽:

auto process = QtConcurrent::run(...);
QTimer& timer = new QTimer();
connect(timer, &QTimer::timeout, [=, &process]() {
  process.cancel();
});
timer->start(3000);

编辑:由于 cancel() 方法不适用于 QtConcurrent::run 函数,最简单的方法是使用某种 isRunning 标志:

bool isRunning = true;
void runnable() {
  while (isRunning) {
    ...
  }
}

auto process = QtConcurrent::run(runnable);
QTimer& timer = new QTimer();
connect(timer, &QTimer::timeout, [=, &isRunning]() {
  isRunning = false;
});
timer->start(3000);
于 2013-11-11T10:28:20.920 回答