【问题标题】:How to make C function wait efficiently for Qthread to finish work?如何让 C 函数有效地等待 Qthread 完成工作?
【发布时间】:2012-04-13 18:06:32
【问题描述】:

我有一个有趣的问题。使用 Qt 处理 C++ 项目。跨平台项目,但在 Win 上开发。

我有一个 C 风格的回调函数。它必须是C风格,我别无选择。 在 C 风格的回调函数中完成的工作是重要且时间敏感的。因此,我有一些 Qthread 线程可以帮助处理工作量。

我没有将运行方案与 Qt 线程一起使用,而是按照 QThread 文档底部的说明使用 QThreads。 http://qt-project.org/doc/qt-5.0/qthread.html#wait

为了清楚起见,我这样使用 QThread:

QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
thread->start();
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

线程是在应用程序开始时创建的,并由 QMetaObject::invokeMethod 提供。

挑战是在 QThread 线程完成它们的工作之前,不让 C 回调函数做“任何事情”(以一种有效的方式)。我想让回调函数等待它不会与工作线程竞争 cpu(所以没有繁忙的虚拟循环)。我也可以使用 sleep() 之类的东西,但这效率不高,因为如果线程“提前”完成,就会浪费睡眠。我想从工作人员那里发送一个信号,但问题是我的回调是一个 C 函数,所以我看不到它如何捕获 Qt 信号。

【问题讨论】:

  • 你不能用 Qt 类包装 C 函数并在那里捕获信号吗?
  • 不,我不能。 C 函数不仅仅是一个 C 函数,它还是一个回调函数。库的一部分。我无法改变它的本质,我只能实现它的身体。我无法控制什么/谁调用回调,我需要将其保留为 C 回调函数。

标签: qt


【解决方案1】:

这个问题很老,但它出现在“相关问题”中。由于这个问题很有趣,而且答案对 Qt 用户很有用,所以我决定给出一个详细的答案。

要等待不同 QThread 中的函数完成,可能有多种策略:

  1. 使用Qt::BlockingQueuedConnection
  2. 使用幼稚的while(anAtomicBoolean){} 循环。
  3. 使用更巧妙的while(anAtomicBoolean){QThread::yieldCurrentThread();} 循环。
  4. 使用QWaitCondition
  5. 使用QSemaphore

策略 2 和 3 看起来很糟糕,因为等待线程必须在等待检查循环条件时处于活动状态。然而,策略 3 的优势在于为其他线程和进程释放 CPU 时间。

其他 3 种策略的优点是在等待时让线程停止,应该是相当的。但是,解决方案 1 和 4 不允许您(或至少很容易)同时在不同线程中运行多个工作程序。所以最好的解决方案是使用信号量。

QSemaphore semaphore(2)
worker1.sem = &semaphore;
worker2.sem = &semaphore;
semaphore.acquire(2);
QMetaObject::invokeMethod(&worker1, "doWork", Qt::QueuedConnection);
QMetaObject::invokeMethod(&worker2, "doWork", Qt::QueuedConnection);
semaphore.acquire(2); // each worker release(1) when done
semaphore.release(2);

为了证明我的主张,我做了一个小基准测试(如果有人感兴趣,我稍后会提供代码)。我使用QueryThreadCycleTime() 来获取等待线程消耗的周期数。像这样:

QueryThreadCycleTime(hThread, &start);
QMetaObject::invokeMethod(&worker, "doWork", Qt::BlockingQueuedConnection);
QueryThreadCycleTime(hThread, &stop);

基准测试结果如下图所示:

【讨论】:

    【解决方案2】:

    好的,考虑这种方法。让您的回调函数除了设置一些全局标志(或增加一些计数器)并显示您的函数是否(或多少次)被调用之外什么都不做。然后实现从 QObject 继承的类(要有槽)。您将其订阅到线程完成信号。发出信号时,您只需调用另一个函数(带有回调函数的逻辑)就被lib调用了很多次。它会起作用吗?

    【讨论】:

    • 听起来像是用锤子把硬币放进存钱罐
    • 是的,听起来像这样,但它满足要求“我想让回调函数等待它不会与工作线程竞争 cpu(所以不忙虚拟循环)。”
    • 谢谢,但这不起作用,因为回调已经被外部来源(操作系统)调用开始......这是一切的催化剂,我仍然需要一种方法来阻止该回调函数从完成并在工作完成之前返回。
    【解决方案3】:

    如果您“手头”有线程,并且您完全确定线程在完成工作后会自行退出。

    http://qt-project.org/doc/qt-4.8/qthread.html#wait

    但是!

    如果你不想让你的线程退出,只想等到 worker 发出一些信号,你可以这样做:

    QEventLoop loop;
    connect(worker, SIGNAL(workFinished()), &loop, SLOT(quit()));
    QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);
    loop.exec();
    

    【讨论】:

    • 是的,我不希望线程死掉,因为制作新线程很昂贵。当您有超过 1 个工人时,这将如何工作?在我看来,在其中一名工作人员完成后,事件循环终止。
    • 然后你需要为自己创建一些“池”对象,它会知道所有的工人,并跟踪他们。它可能会使用 QEventLoop 方法本身并检查是否所有工作人员都完成了
    猜你喜欢
    • 2020-11-14
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多