如何使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信号的。

+1

不能老是你包C函数的使用Qt类和捕获信号有:

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

的基准测试结果如下图呈现? – dvvrd 2012-04-13 18:16:53

+0

不,我不能。 C函数不仅仅是一个C函数,它是一个回调函数。一部分的lib。我不能改变它的性质,我只能执行它的身体。我对什么/谁调用回调没有影响力,我需要保持一个C回调函数。 – L123 2012-04-13 18:26:20

好的,考虑这样的问题。让你的回调函数除了设置一些全局的标志(或增加一些计数器)并且显示你的函数是否被调用(或多少次)之外什么也不做。然后从QObject继承的imlement类(有槽)。您订阅它以线程完成信号。当信号发出时,你只需调用另一个函数(用你的回调函数的逻辑),那么它就被lib调用了很多次。它会起作用吗?

+0

这听起来像用锤子把一枚硬币放入存钱罐 – 2012-04-13 18:47:27

+0

是的,它听起来像这样,但它满足要求“我想使回调函数等待,以便它不会与CPU竞争工作者线程(所以不需要繁忙的虚拟循环)“。 – dvvrd 2012-04-13 18:55:21

+0

谢谢,但这是行不通的,因为回调已经被外部源(操作系统)所调用...这是所有事情的催化剂,我仍然需要一种方法来阻止从完成和返回的回调函数工作完成之前。 – L123 2012-04-13 19:00:08

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

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(); 
+0

是的,我不希望线程死掉,因为新线程很昂贵。当你有超过1名工人时,这将如何工作?在我看来,其中一名工作人员完成后,事件循环终止。 – L123 2012-04-13 19:08:30

+0

然后,您需要为自己创建一些“泳池”对象,这将会了解所有工作人员,并保持对他们的跟踪。它可以使用QEventLoop aproach本身,只是检查是否所有的工人完成 – 2012-04-14 05:19:27

问题是安静了,但它想出了“相关问题”。由于这个问题很有趣,而且答案对Qt用户来说很有用,所以我决定给出一个详细的答案。

做上的功能等不同的QThread来完成,多个策略是可能的:

  1. 使用Qt::BlockingQueuedConnection
  2. 使用幼稚的while(anAtomicBoolean){}循环。
  3. 使用更巧妙的while(anAtomicBoolean){QThread::yieldCurrentThread();}循环。
  4. 使用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()来获得等待线程消耗的周期数。就像这样: Result graph