1

我有一个有趣的问题。使用 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 信号。

4

3 回答 3

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);

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

于 2017-02-08T22:30:47.153 回答
0

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

于 2012-04-13T18:46:30.020 回答
0

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

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();
于 2012-04-13T18:46:48.487 回答