【问题标题】:How to stop a long-running function prematurely?如何过早停止长时间运行的函数?
【发布时间】:2021-02-18 01:42:29
【问题描述】:

我正在研究一种绘图算法。为此,我从主 GUI 线程中的 DAQ 板获取数据,然后将数据发送到工作线程进行处理,工作线程发出带有新 QImage 的信号,我将其显示为我的图形用户界面。问题在于函数,我们称之为generateImage(),计算和生成QImage 需要很长时间(约50-70 毫秒,取决于数据长度),并且在这段时间之间可能会有另一组数据到达要求工作线程从头开始重新计算绘图。我希望generateImage() 放弃计算并在新数据到达时从头开始重新开始计算。我的方法是设置一个成员布尔变量,我们称之为b_abort_,并检查它是否在generateImage()return 内设置为true,如果它是真的,在generateImage() 之外它始终保持为真,我只设置它在调用 generateImage() 之前为 false。

所有这些都发生在工作线程中,我将QObject 子类化并使用moveToThread() 将其移至工作线程。

开始计算的函数:

void WorkerThread::startCalc()
{
    b_abort_ = false;
    generateImage();
    // if b_abort_ is set to true, generateImage() will return prematurely 
    if(b_abort_)
        emit calcFinished();
    else
        b_abort_ = true;
}

进行所有计算并生成图像的功能:

void WorkerThread::generateImage()
{
    /* Calculation of some parts */
    for(int ii = 0; ii < Rawdata.length(); ++ii) // Starting main time consuming loop
    { 
        if(b_abort_)
            return;
        /* Perform calculation for one data point from Rawdata */
    }

    // data calculation complete, now it's time to generate QImage
    // before that I check if b_abort_ is set to true
    if(b_abort_)
        return;

    for(int ii = 0; ii < CalculatedData.length(); ++ii) // plotting calculated data on QImage
    {
        if(b_abort_)
            return;
        /* plot one data point from CalculatedData vector */
    }

    // generation of QImage finished, time to send the signal
    emit renderedPlot(image); // image is a QImage object
}

在我的工作线程中,我有一个从主 GUI 线程接收数据的插槽,它被配置为 Qt::QueuedConnection(默认)作为连接类型:

void WorkerThread::receiveData(QVector<double> data)
{
    if(!b_abort_) // check if calculation is still running
    { 
        QEventLoop loop;
        connect(this, &WorkerThread::calcFinished, &loop, &QEventLoop::quit);
        b_abort_ = true; // set it to true and wait for calculation to stop
        loop.exec();
        // start new calculation
        RawData = data;
        startClac();
    }
    else
    {
        RawData = data;
        startClac();
    }
}

当我在我的主 GUI 线程中使用这种方法时,generateImage() 函数会阻塞所有事件循环,并且我的 GUI 冻结,这让我认为在单个线程(主 GUI 线程或工作线程)中只有一个函数可以一次运行,因此在线程的事件循环返回以处理其他函数之前,不会应用 b_abort_ 中的任何更改。使用WorkerThread 时,很难验证这是否有效,有时它可以正常工作,而其他时候它会产生 bad allocation 错误,看起来好像它不起作用(尽管可能是因为完全不同的原因,我不确定)。我想问问您的意见,这是过早停止长时间运行的计算的正确方法吗?有没有其他比我目前的方法更强大的方法可以使用?

【问题讨论】:

  • 这能回答你的问题吗? C++0x thread interruption
  • @UlrichEckhardt 不,我正在寻找的是从调用它的同一个线程内部停止一个长时间运行的函数,而不是从另一个线程停止/终止。
  • “在单个线程内 [...] 一次只能运行一个函数”——就是这种情况。我宁愿换一种说法:一个线程从一个地方执行代码。当然,那个地方可以在不同的功能之间移动,但不能同时在两个地方。
  • 那个线程已经在做某事,所以不是真的。您可以使用 POSIX 信号/处理程序,但它的可移植性较差(并且标准 C++ 接口还不够,因为您需要 pthread_kill 和/或 pthread_sigmask 才能完成这项工作)。这些都不会自动与 Qt 信号一起工作,因此您仍然需要在主线程中执行一些代码来发送该信号。
  • 重要的部分不是哪个线程设置它(但请再次阅读下面有关“以某种方式同步”的答案),而是执行某些操作的线程无法设置它,反之亦然。一个线程可以在不同的事情之间交替,但它不能同时做两件事。顺便说一句:我不确定这是不是误解,但是如果您在某处有 C++ QThreadstd::thread 对象,那不是线程!这更像是std::fstream 是一个文件,它是一个 C++ 对象,可用于与线程交互,以防万一你的误解。

标签: c++ multithreading qt c++17


【解决方案1】:

如何提前停止另一个线程中长时间运行的函数?

您说得对,唯一明智的做法是定期检查长时间运行的线程是否应该提前停止。

请注意,您要检查的标志必须是原子的,或者受互斥体保护,或者以其他方式同步。否则,工作线程检查变量并且永远不会看到值更改是完全合法的(不,您不能为此使用volatile)。

...这让我觉得在单个线程(主 GUI 线程或工作线程)内一次只能运行一个函数...

是的,这就是线程!它是一个单一的、线性的执行线程。它不能同时做两件事。同时做两件事是拥有多个线程的全部原因。

方法应该是有一个等待工作的工作线程和一个只向它发送异步消息的主线程(开始使用此数据生成图像,或中断处理并使用此数据重新开始,或者随便)。

如果主线程调用了一个应该在工作线程中发生的函数,那么,你故意在主线程中开始执行它,而主线程不会做任何事情,直到它返回。就像其他所有功能一样。


顺便说一句,您的设计存在一个问题:如果一张图片不断被新数据打断,它可能永远无法完成生成。

通常的解决方案是双缓冲:让工作线程完成当前图像的生成,而主线程为下一个图像积累数据。当工作人员完成一张图像后,可以将其传回主线程进行显示。 然后 worker 可以开始处理下一个,因此它会占用主线程为其准备的“脏”更新缓冲区。后续更新将再次添加到 next 图像的(现在为空的)缓冲区中。

【讨论】:

  • 好的,我明白了,所以使用主线程检查并更改b_abort_ 的值,然后工作线程内的generateImage() 函数将能够提前返回,对吗?关于双缓冲,它对我来说不是那么有用,因为大多数时候我必须停止并重新计算是当用户与绘图交互时,例如滚动或缩放会不断生成事件,所以我需要停止我的计算过早地在我的工作线程中。
  • 我希望我的工作线程尽可能自给自足。我绝对没有办法从运行函数的同一线程中实现相同的函数过早返回?
  • 您正在考虑在多线程应用程序的单个线程中实现协作多任务或协同例程。这很复杂,而且看起来并不值得。您可以让工作对象拥有 两个 线程 - 一个长时间运行的计算线程和一个空闲的看门狗线程,您可以向其发送中止请求 - 但这似乎并不比仅仅拥有主线程自己设置中止标志。
猜你喜欢
  • 2014-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-04
  • 2012-03-15
  • 2010-11-12
  • 1970-01-01
  • 2011-11-23
相关资源
最近更新 更多