我正在为一些计算密集型任务(机器视觉)开发一个C ++类库。
// I am a part of a Qt-agnostic library
class Cruncher
{
/* ... */
public:
void doStuff();
};
然后有一个使用该库的Qt GUI。我正在创建一个工作线程来调用库中繁重的例程:
// I am a part of a Qt-based GUI which utilizes the library
class Worker : public QThread
{
/* ... */
protected:
virtual void run()
{
/* ... */
Cruncher c;
for (int i = 0; i < count; ++i)
c.doStuff(); // takes some time, and while it's working
// it should communicate status changes which should
// become visible in the GUI
}
};
现在在doStuff()里面发生了很多事情,我想在不等待doStuff()返回的情况下向用户提供一些反馈。首先,可能会有一些更精细的进度报告,而不是在每次调用doStuff()之后将仪表增加一步。此外,doStuff()可能会遇到非关键性故障,这会让它继续工作的一部分,但是当Cruncher正在工作时,我希望在GUI中出现一条消息(而且当前工作人员忙于调用doStuff())。
我希望库保持Qt独立性,所以我不愿意向Cruncher添加信号和插槽。还有其他任何方式可以让它向GUI提供反馈,以便在它不是Qt类时报告其工作吗?
我正在考虑创建一个QTimer,它会在Worker运行时以固定的间隔轮询Cruncher的某些“status”和“errorMsg”成员,但这似乎非常不理想。
答案 0 :(得分:3)
我发布了自己的答案,因为虽然我接受了@Nim的建议,但我希望答案更加冗长,如果有人应该有同样的问题,那就更有用了。
我在库中创建了消息调度程序的框架:
// doesn't need to know about Qt
class MessagePort
{
public:
virtual void message(std::string msg) = 0;
};
接下来,我将此对象的句柄添加到Cruncher和spiced doStuff(),偶尔调用message():
// now with Super Cow powers!
class Cruncher
{
protected:
MessagePort *msgPort_;
public:
Cruncher(MessagePort *msgPort) : msgPort_(msgPort) {}
void doStuff()
{
while(...)
{
/*...*/
msgPort_->message("Foo caused an overload in Bar!");
}
}
};
最后,我使用所有必要的Qt优点在GUI中制作了MessagePort的实现:
class CruncherMsgCallback : public QObject, public MessagePort
{
Q_OBJECT
public:
CruncherMsgCallback() : QObject(), MessagePort()
{
connect(this, SIGNAL(messageSignal(const QString &)),
GUI, SLOT(messageShow(const QString &)),
Qt::QueuedConnection);
}
virtual void message(std::string msg)
{
emit messageSignal(QString::fromStdString(msg));
}
signals:
void messageSignal(const QString &msg);
};
最后,当Worker创建一个Cruncher实例时,它还会给它一个指向工作MessagePort的指针:
class Worker
{
protected:
virtual void run()
{
CruncherMsgCallback msgC;
Cruncher c(&msgC); // &msgC works as a pointer to a
// generic MessagePort by upcasting
c.doStuff(); // Cruncher can send messages to the GUI
// from inside doStuff()
}
};
答案 1 :(得分:1)
使用回调函数(类)等,并在构造期间传递它。您需要报告的事项,通过回调进行报告。
答案 2 :(得分:1)
您可以安全地从run()
方法发出信号,我认为这是将信息从工作线程传递到主线程的最佳方式。只需将信号添加到QThread子类中(如果您不确定QThread线程的工作原理,请避免添加插槽)。
最好将这些信号的连接明确排队,以避免出现问题。虽然默认的自动连接类型也应该工作,并且排队信号会发出,但我认为最好在这种情况下明确。实际上也是直接信号应该这样工作,但是你必须自己处理线程安全,而不是让Qt为你处理它,你不能连接到使用任何QtGui类的插槽,这些类只在主要工作线程,所以最好坚持排队连接。
要将简单信息传递给run()
方法,并且如果不需要立即反应,可以使用一些共享的QAtomicInt
变量或类似的变量作为标志,工作者线程在方便时检查。稍微复杂的方法,仍然需要轮询,是使用互斥锁保护的共享数据结构。更复杂的通信方式将涉及某种消息队列(就像Qt在主线程的事件循环中使用,当你向该方向发出信号时)。