首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >插槽中的QMutex

插槽中的QMutex
EN

Stack Overflow用户
提问于 2015-02-12 09:18:30
回答 2查看 1.7K关注 0票数 0

假设有以下QT代码(QT5.3.1):

代码语言:javascript
复制
void SenderClass::runSignal()
{
    emit mySignal();
}

void ReceiverClass::ReceiverClass()
{
    ...
    connect (senderClassRef, SIGNAL(mySignal()), this, SLOT(mySlot()) );
}

void ReceiverClass::mySlot()
{
    //Long operation executions
    Sleep(1000);
    qDebug() << "1";
    Sleep(1000);
    qDebug() << "2";
    Sleep(1000);
    qDebug() << "3";
}

在连续调用runSignal()时,控制台显示的内容如下:

1 2 1 3 2 3

这两个类生活在同一个线程中。我必须在插槽中使用QMutexLocker吗?或者有另一种方法可以得到有序的输出,比如:

1 2 3 1 2 3

因此,如果仍然有一个正在执行的函数,则阻止mySlot()函数调用。

更新

下面是一个真正的代码片段。发件人:

代码语言:javascript
复制
//Sender.h

#include <QtWidgets/QWidget>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextBrowser>

class Receiver;

class Sender : public QWidget
{
    Q_OBJECT

public:
    Sender(QWidget *parent = 0);

    QPushButton *pushButton;
    QTextBrowser *textBrowser;
    Receiver* receiver;

signals:
    void buttonClickedSignal();

public slots:
    void ButtonClickedSlot();
};

//Sender.cpp

#include "Sender.h"
#include "Receiver.h"

#include <QObject>

Sender::Sender(QWidget *parent)
    : QWidget(parent)
{
    pushButton = new QPushButton(this);
    pushButton->setObjectName(QStringLiteral("pushButton"));
    pushButton->setGeometry(QRect(150, 30, 75, 23));
    pushButton->setText("Button");
    textBrowser = new QTextBrowser(this);
    textBrowser->setObjectName(QStringLiteral("textBrowser"));
    textBrowser->setGeometry(QRect(50, 90, 256, 192));

    receiver = new Receiver(this);

    QObject::connect(pushButton, SIGNAL(clicked()), this, SLOT(ButtonClickedSlot()));
}

void Sender::ButtonClickedSlot()
{
    emit buttonClickedSignal();
}

接受者:

代码语言:javascript
复制
//Receiver.h
#include "Sender.h"

class Receiver : QObject
{
    Q_OBJECT

public:
    Receiver(Sender *sender);

    Sender *m_sender;

public slots:
    void ReceiverSlot();
};

//Receiver.cpp
#include "Receiver.h"

#include <QObject>
#include <QThread>
#include <QApplication>

Receiver::Receiver(Sender *sender)
{
    m_sender = sender;

    QObject::connect(m_sender, SIGNAL(buttonClickedSignal()), this, SLOT(ReceiverSlot()), Qt::QueuedConnection);
}

void Receiver::ReceiverSlot()
{
    m_sender->textBrowser->append("1");
    QThread::msleep(100);
    qApp->processEvents();
    m_sender->textBrowser->append("2");
    QThread::msleep(100);
    qApp->processEvents();
    m_sender->textBrowser->append("3");
    QThread::msleep(100);
    qApp->processEvents();
    m_sender->textBrowser->append("4");
    QThread::msleep(100);
    qApp->processEvents();
}

单击该按钮会很快在QTextBrowser中生成不连续的数字,即使设置了QueuedConnection。

更新2

我想要实现的是对ReceiverSlot的排队访问。用户可以随心所欲地按下按钮(当他想要的时候,以任何速度)。ReceiverSlot不能错过任何事件。我需要某种类型的事件队列,以便ReceiverSlot中的(long)操作总是被执行,可能会延迟,但会被执行。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-02-13 08:02:50

摆脱QApplication::processEvents,将冻结GUI的长时间操作移动到一个新线程。例如,可以使用移动到新线程的worker对象。有一个这在文档中很好的例子。

您还应该阅读来了解更多关于Qt中的线程和事件的信息。与您在强迫事件调度部分中解释的情况完全相同:

当“通过其他路径”重新进入事件循环时要非常小心:它可能导致不必要的递归!让我们回到Button示例。如果我们在doWork()槽内调用QCoreApplication::processEvents(),并且用户再次单击该按钮,则将再次调用doWork()槽:

  • 主要(int,char **) QApplication::exec()
  • QWidget::event(QEvent *)
  • 按钮::mousePressEvent(QMouseEvent *)
  • 按钮::clicked()
  • Worker::doWork() // first,内部调用
  • QCoreApplication::processEvents() //我们手动分派事件和…
  • QWidget::event(QEvent *) //另一个鼠标单击被发送到Button…
  • 按钮::mousePressEvent(QMouseEvent *)
  • 按钮::clicked() //它再次发出clicked()…
  • 工人:多工作() // DANG!我们已经重新回到我们的位置了。

通常,您希望避免使用QApplication::processEvents并创建本地事件循环。如果您的代码中有这些,那么您应该重新设计它,这样您就不必使用这些了。

Worker对象从文档中进行检查:

代码语言:javascript
复制
 class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;

 public slots:
     void doWork(const QString &parameter) {
         // ...
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
         connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
         connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };
票数 1
EN

Stack Overflow用户

发布于 2015-02-12 15:26:59

把我的意见变成一个恰当的答案:

如果在您的插槽中调用qApp->processEvents()时,有一个鼠标按事件等待被传递到按钮,则它将被传递,clicked()信号将再次发出,因此您的插槽将再次被调用。

QMutex不会有帮助,因为不涉及线程。添加一个很可能会导致程序死锁。

使用Qt::QueuedConnection没有帮助,因为这会将一个事件发布到队列中,在处理该事件时,将导致调用插槽。因此,充其量,它只是将其推迟到下次调用qApp->processEvents()时。

在示例的上下文中,我的建议是摆脱排队的连接,而是在插槽工作时禁用按钮。这将防止再次发出clicked()信号,并显示用户此时不应该试图单击按钮。

如果出于任何原因您不想这样做,您可以添加一个标志到您的接收类,以防止递归调用。像这样的东西会起作用的,尽管它不是很漂亮:

代码语言:javascript
复制
void Receiver::ReceiverSlot()
{
  if( m_inSlot )
    return;
  m_inSlot = true;
  // do work
  m_inSlot = false;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28473563

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档