Qtマルチスレッド信号とスロットおよびC++11のバインドおよびQMetaObject::invokeMethod

13869 ワード

C++11で信号とスロットをバインドすると、コードの可読性が向上し、柔軟性が向上します注意:connect()で接続タイプを宣言できます.デフォルトではAutoConnectionクリックで「接続タイプ」の紹介にスクロールします↓

テストインタフェース:

#include 
#include 
class MyWindow : public QWidget
{
    Q_OBJECT

public:

    typedef  std::function<void(bool checked)>  fuc1;
    MyWindow(QWidget *parent = 0);
    ~MyWindow();
    QPushButton *  btn;
    QLabel* label;  QVBoxLayout* Vbox; 
    QGridLayout* grid;
    void click2(bool checked);
    void set(QString s);


public slots:
    void On_TestBtn_Cliked();
    void set2(QString s);


};


MyWindow::MyWindow(QWidget *parent) : QWidget(parent) { label = new QLabel(QStringLiteral(" ")); btn = new QPushButton("Test"); grid = new QGridLayout(); grid->addWidget(btn,0,0); Vbox = new QVBoxLayout(); Vbox->addLayout(grid); Vbox->addWidget(label); setLayout(Vbox); fuc1 fu = std::bind(&MyWindow::click2, this, std::placeholders::_1); connect(btn, &QPushButton::clicked, this, fu); } MyWindow::~MyWindow() { } void MyWindow::set(QString s) { qDebug() <<"set:"<< QThread::currentThreadId(); label->setText(s); } void MyWindow::set2(QString s) { qDebug() << "set:" << QThread::currentThreadId(); label->setText(s); } void MyWindow::click2(bool checked) { qDebug() << "clcik2:"<<:currentthreadid mythread="" th="<span" class="hljs-keyword">new MyThread(this); th->start(); } void MyWindow::On_TestBtn_Cliked() { qDebug() << "click"; }

スレッド:

C++11 std::bindを使用して、バインド信号とスロット関数はslotsを宣言する必要はありません:

#pragma once
#include 
#include  
class MyThread : public QThread { Q_OBJECT public: QObject* window; void run(); MyThread(QObject* parent=NULL); ~MyThread(); signals: void print(QString); }; 
#include "MyThread.h"
#include "MainWindow.h"

MyThread::MyThread(QObject* parent):QThread(parent)
{
    window = parent;
    MyWindow*  w = (MyWindow*)parent; 
 std::function<void(QString)>  fu = std::bind(&MyWindow::set, w, std::placeholders::_1);
    connect(this, &MyThread::print, w,fu); 
}


MyThread::~MyThread()
{
}


void  MyThread::run()
{
    qDebug() << "Thread Begin";
    int cout = 0;
    while (true)
    {
        qDebug() << "MyThread::run:" << QThread::currentThreadId();
        emit print(QString::number(cout));
        Sleep(1);
        cout++;
    }
}


printバインドされたスレッドidは、プライマリスレッドと同様にスレッドに安全であることがわかります.

簡略化された形式:

    connect(this, &MyThread::print, w, [=](QString s) {

        w->set(s);
        qDebug() << "connet fuc:" << s << " " << QThread::currentThreadId();
        //   id , 
    });

QMetaObject::invokeMethod呼び出しスロットを使用する:スロット関数はslotとして宣言する必要があります:

void  MyThread::run()
{
    qDebug() << "Thread Begin";
    int cout = 0;
    while (true)
    {
        qDebug() << "MyThread::run:" << QThread::currentThreadId();
      QMetaObject::invokeMethod(w, "set2", Q_ARG(QString, QString::number(cout)));
        Sleep(1);
        cout++;
    }
}


コントロールのスロットに直接関連付け:

MyThread::MyThread(QObject* parent):QThread(parent)
{
    window = parent;
    MyWindow*  w = (MyWindow*)parent;

    connect(this, &MyThread::print, w, std::bind(&QLabel::setText, w->label , std::placeholders::_1));
}

コントロールのスロットに直接関連付けられ、文字をフィルタできます。

connect(this, &MyThread::print, w->label, [=](QString s)
 {
 //if(s.indexOf(xxxxx))  
 qDebug() << "Bind Fuc:" << QThread::currentThreadId();
 w->label->setText(s);
 });

または
    connect(this, &MyThread::print, w, [=](QString s)
    {
        //if(s.indexOf(xxxxx))  
        qDebug() << "Bind Fuc:" << QThread::currentThreadId();
        w->label->setText(s);
    });

QMetaObject::invokeMethod


インタフェースのスロットset 2を変更するには:
void MyWindow::set2(QString s, int pid)
{
    if (pid ==(int)QThread::currentThreadId())
    {
        qDebug() << QStringLiteral("  ::" )<< s;
        qDebug() << "id_1:" << pid << " id_2:" << QThread::currentThreadId();
    }


}

スレッド:

void  MyThread::run()
{
    qDebug() << "Thread Begin";
    int cout = 0;
    int _pid;
    while (true)
    {
        _pid = (int)QThread::currentThreadId();
        qDebug() << "MyThread::run:" << _pid;
       // emit print(QString::number(cout));

        MyWindow*  w = (MyWindow*)window;
        QMetaObject::invokeMethod(w, "set2", Qt::ConnectionType::QueuedConnection,
            Q_ARG(QString, "QueuedConnection"), Q_ARG(int, _pid)
        );
        QMetaObject::invokeMethod(w, "set2", Qt::ConnectionType::AutoConnection,
            Q_ARG(QString,"AutoConnection"), Q_ARG(int, _pid)
        );
        QMetaObject::invokeMethod(w, "set2", Qt::ConnectionType::BlockingQueuedConnection, 
            Q_ARG(QString, "BlockingQueuedConnection"),Q_ARG(int, _pid)
        );
        QMetaObject::invokeMethod(w, "set2", Qt::ConnectionType::DirectConnection,
            Q_ARG(QString, "DirectConnection"), Q_ARG(int, _pid)
        );
        QMetaObject::invokeMethod(w, "set2", Qt::ConnectionType::UniqueConnection, 
            Q_ARG(QString, "UniqueConnection"), Q_ARG(int, _pid)
        );
        Sleep(1);
        cout++;
    }
}

このように、DirectConnection接続タイプのスレッドIDはメインスレッドと異なり、スレッドと同じで、スレッドセキュリティではありません.他の接続タイプはしばらく方法テストが見つかりません.例のある友达は私と交流してください.ありがとうございます.
公式説明の接続タイプです.

説明:信号:送信者スロット:受信者信号とスロットが存在するスレッドは、connnetを呼び出すときに存在するスレッドではなく、スレッドを作成します。


AutoConnection


受信者がスレッドに住んでいる場合、Qt::DirectConnectionを使用します.そうでない場合はQt::QueuedConnectionを使用します.接続タイプ送信時に決定します.解釈:受信者がスレッドに住んでいる場合、Qt::DirectConnectionを使用します.そうでない場合はQt::QueuedConnectionを使用します.接続タイプ送信時に決定します.すなわち,信号とスロットが同一スレッドである場合にはQt::DirectConnectionを呼び出し,そうでない場合にはQt::QueuedConnectionを呼び出すと自動的に判断する.

DirectConnection


呼び出しスロットが直ちに信号を発したとき.スロットは信号スレッドで実行されます.

QueuedConnection


非同期と理解できますか?スロットが受信イベントループを呼び出すスレッドに送信されると、スロットは受信者のスレッドで実行される.すなわち、この接続タイプは、スロットが存在するスレッドイベントに信号を送信しても、スロットが存在するスレッドイベントの処理が完了するのを待つことなく、スロットが存在するスレッドイベントループがこの信号を処理すると、対応する動作が実行される

BlockingQueuedConnection


同期として理解でき、同期まで現在のスレッドをブロックしますか?スロットが受信イベントループを呼び出すスレッドに送信されると、スロットは受信者のスレッドで実行される.すなわち、この接続タイプは、スロットが存在するスレッドイベントに信号を送信するだけでなく、スロットが存在するスレッドイベントの処理が完了するのを待つ.スロットが存在するスレッドイベントループは、この信号を処理すると、対応する操作を実行し、信号が存在するスレッドは次の行のコードを継続する

UniqueConnection


資料が少なすぎて、このタイプの大体の用途がわかりません...これは、上記のいずれかの接続タイプと組み合わせて、ビット単位またはを使用できるフラグです.Qt:UniqueConnectionの場合、QObject:connect()は、接続がすでに存在する場合(つまり、同じ信号が同じスロットの同じオブジェクトに接続されている場合)に失敗します.このフラグはQt 4.6に導入された.