如何使C函数有效地等待Qthread完成工作?
我有一个有趣的问题。使用Qt处理C++项目。跨平台项目,但在Win上发展。如何使C函数有效地等待Qthread完成工作?
我有一个C风格的回调函数。它需要是C风格,我没有选择它。 在C风格的回调函数中完成的工作是重要且时间敏感的。因此,我有一些Qthread线程可以帮助解决工作负载。
我没有使用Qt线程的运行方案,而是使用QThreads作为解释在QThread文档的底部。 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信号的。
好的,考虑这样的问题。让你的回调函数除了设置一些全局的标志(或增加一些计数器)并且显示你的函数是否被调用(或多少次)之外什么也不做。然后从QObject继承的imlement类(有槽)。您订阅它以线程完成信号。当信号发出时,你只需调用另一个函数(用你的回调函数的逻辑),那么它就被lib调用了很多次。它会起作用吗?
如果您有“牵手”线程,并且您完全确定该线程在完成工作后将自行退出。
http://qt-project.org/doc/qt-4.8/qthread.html#wait
但是!
如果你不想让你的线程退出,只是要等到工人发出一些新葛你可能会做这样的事情:
QEventLoop loop;
connect(worker, SIGNAL(workFinished()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);
loop.exec();
是的,我不希望线程死掉,因为新线程很昂贵。当你有超过1名工人时,这将如何工作?在我看来,其中一名工作人员完成后,事件循环终止。 – L123 2012-04-13 19:08:30
然后,您需要为自己创建一些“泳池”对象,这将会了解所有工作人员,并保持对他们的跟踪。它可以使用QEventLoop aproach本身,只是检查是否所有的工人完成 – 2012-04-14 05:19:27
问题是安静了,但它想出了“相关问题”。由于这个问题很有趣,而且答案对Qt用户来说很有用,所以我决定给出一个详细的答案。
做上的功能等不同的QThread来完成,多个策略是可能的:
- 使用
Qt::BlockingQueuedConnection
。 - 使用幼稚的
while(anAtomicBoolean){}
循环。 - 使用更巧妙的
while(anAtomicBoolean){QThread::yieldCurrentThread();}
循环。 - 使用
QWaitCondition
。使用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()
来获得等待线程消耗的周期数。就像这样:
不能老是你包C函数的使用Qt类和捕获信号有:
的基准测试结果如下图呈现? – dvvrd 2012-04-13 18:16:53
不,我不能。 C函数不仅仅是一个C函数,它是一个回调函数。一部分的lib。我不能改变它的性质,我只能执行它的身体。我对什么/谁调用回调没有影响力,我需要保持一个C回调函数。 – L123 2012-04-13 18:26:20