【问题标题】:Qt: QFuture/QtConcurrent timeout function callQt:QFuture/QtConcurrent超时函数调用
【发布时间】:2013-11-23 01:33:37
【问题描述】:

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

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

我正在寻找类似的东西:

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

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

【问题讨论】:

  • 你需要使用QtConcurrent吗?否则,我将创建一个 QThread 子类,在其 run 方法中调用该函数,并在超时时使用 wait。如果线程卡住了,您甚至可以terminate 线程,但请谨慎使用! (见文档)
  • @Laszlo 你有没有找到类似 QtConcurrent::run(............,30000 /*30 seconds timeout)*/;正在寻找相同的东西。
  • @Dhanesh:你问错人了。 :)
  • 哎呀...想问这个给 tonka... :(

标签: c++ qt


【解决方案1】:

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

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

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

【讨论】:

    【解决方案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 执行此操作,使用 QFutureWatcherQTimer 和帮助程序 QEventLoop。设置您的并发运行,并使用观察者观察结果。使用计时器设置 Timout,并在事件循环中等待直到完成。

    // 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...
    }
    

    【讨论】:

    • 这行不通。来自 Qt 文档:“请注意,并非所有异步计算都可以取消。例如,不能取消 QtConcurrent::run() 返回的未来”
    • @IvanGrynko 好的,不能取消,我忽略了那条评论。但是当函数陷入无限循环时,该过程将继续,调用者可以发现这个事实。但是有了这个限制,我不建议在生产代码中使用它,仅用于测试目的
    • 谢谢king_nak。这正是我们正在寻找的东西。代码有效,但我在 qt-documentation 中读到无法取消 Qt::Concurrent::run 调用的 :-( 所以当我调用 future.cancel 时,它不会真正取消“进程”=> 那不会解决我的问题:-(还有其他想法吗?
    【解决方案3】:

    您可以将 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);
    

    【讨论】:

    • 这行不通。来自 Qt 文档:“请注意,并非所有异步计算都可以取消。例如,不能取消 QtConcurrent::run() 返回的未来”
    • 是的,cancel() 方法实际上是行不通的,但想法是设置取消标志,这样正在运行的线程就会知道何时停止。这可以使用 QTimer 轻松完成。
    • 您的第二个代码 sn-p 存在数据竞争。对isRunning 的访问是不同步的。
    【解决方案4】:

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

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

    如果你仍然想要超时,你可以用循环在你的函数中添加这样的东西:

    QTime t;
    t.start();
    
    // it's your loop
    while( true ) {
    
        if( t.elapsed() > 30*1000 ) {
            return 1;
        }
        ...
    }
    

    【讨论】:

    • > 我认为您需要找出发生无限循环的原因,而不是试图解决症状。绝对,但我的问题是服务器上的单元测试冻结,所以所有进程都被冻结在测试套件中:-(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-24
    • 2012-08-13
    • 2023-03-21
    • 2017-06-25
    • 1970-01-01
    相关资源
    最近更新 更多