我会尽量明确。我创建了一个Qt应用程序,它有一些按钮和一个QTextEdit。接下来我创建一个pthread。并提供指向MainWindow的指针作为参数。像这样:
MainWindow w;
pthread_create(&rThread,NULL,treat,&w);
treat是创建线程时执行的函数。现在,如果我有一个名为myButton的pushButton,我会在treat函数中做这样的事情:
void *treat(void *arg)
{
MainWindow *win = (MainWindow*)arg;
win->ui->myButton->setEnabled(false);
close(pthread_self());
}
它会正常工作,我的应用程序中的myButton将会禁用。但是,如果我做这样的事情:
void *treat(void *arg)
{
MainWindow *win = (MainWindow*arg;
win->ui->editText->setText("random string");
close(pthread_self());
}
我的应用程序将崩溃并出现以下错误:
QObject:无法为不同的父级创建子级 线。 (Parent是QTextDocument(0x23af2e0),父亲的主题是 QThread(0x209a290),当前线程为QThread(0x7f7eec000af0) 程序意外地完成了。
据我所知,Ui生活在主线程中,并且可能在我创建的线程中不可访问,尽管事实上我提供了主窗口的指针到这个线程。但为什么禁用该按钮有效?我很迷茫。我使用QThread的原因是因为我们的老师告诉我不要这样做。我必须使用pthreads。我如何将这样的更改从pthread应用到editText? 我怎么能从一个pthread发送一个信号到Ui“生活”的另一个线程。先谢谢你们。
答案 0 :(得分:3)
一般来说,从QObject
以外的线程调用任何的object->thread()
(或派生类)方法是错误的 - 除非它们被设计和记录到是线程安全的。 Qt中有一些方法明确记录为线程安全,例如QCoreApplication::postEvent
。
您面临的行为是由于从非gui线程访问QWidget
方法。它是未定义的行为,因此某些方法可能会崩溃,有些方法可能会崩溃,但即使它们不存在,它仍然是未定义的行为,您无法指望它。我们所知道的,这可能取决于月亮的相位。
另一个线程唯一安全的做法是将事件发布到对象。当您在另一个线程中的对象上使用QMetaMethod::invoke
或QMetaObject::invokeMethod
时,Qt会在内部将QMetaCallEvent
发布到该对象。由于发布事件是线程安全的(可以从其他线程完成),因此可以使用其他线程中的任何一种调用方法。 QObject::event()
通过执行适当的方法调用来对此类事件做出反应。
所以,你可以从另一个线程做的唯一事情是:
QMetaObject::invokeMethod(win->ui->editText, "setText", Q_ARG(QString, "random string"));
唉,这是糟糕的设计,因为你将MainWindow的内部细节(如ui
指针)暴露在外面。你应该做的是在窗口上有一个setEditText
插槽:
MainWindow : public QWidget {
...
public:
Q_SLOT void setEditText(const QString & str) {
ui->editText->setText(str);
}
...
};
然后,从另一个主题,你做:
QMetaObject::invokeMethod(win, "setEditText", Q_ARG(QString, "random string"));
我完全同意Marek R建议您在QThread
可用时不使用pthread。
答案 1 :(得分:1)
首先在没有必要时混合库是坏习惯。 Qt提供了QThread
,非常方便QtConcurrent
。
其次这是糟糕的设计。创建一些QObject
将在线程中处理你的计算,并在将结果传递给UI(主线程)时发出信号。然后创建连接,Qt将处理其余的东西使其线程安全(默认情况下,如果信号在线程之间传递,它将排队连接)。
你的Qt并发代码:
void *treat(SomeClass *arg) {
arg->doStuff();
}
QtConcurrent::run(treat, someObject);