2012-04-27 43 views
3

...静的クラスと非メインスレッドから呼び出されます。要するに、私は静的メンバーとして別の静的クラス "tobj"を持つ "sapp"クラスを持っています。静的な順序の初期化の失敗を避けるために、tobjはsappのメソッドの中で宣言され、sappのメソッドはtobjのインスタンスのポインタを返します。 私の問題は、tobjにはコンストラクタで開始されるべきタイマーがあり、tobjは非メインスレッドによって作成されることです。 QTimerは、メインスレッド以外のスレッド(または、私が推測するイベントループを持たないスレッド)で起動することはできません。そのため、私はQMetaObject :: invokeMethod + Qt :: QueuedConnection経由でQTimer :: startを呼び出してスレッドの問題を回避しますが、動作しません.QTimer :: startは呼び出されません。 QTimerの親(この場合はtobj)が静的として宣言されているため、QTimer :: startは呼び出されません。私がtobjを非静的メンバーとして宣言すれば、すべて正常に動作します。QMetaObject :: invokeMethodが動作しないとき...

私はQtの内部をよく理解していませんが、これはバグか、何か間違っていますか?

はここのコードです:

class tobj : public QObject 
{ 
    Q_OBJECT 

    QTimer timer; 
private slots: 
     void timeout(); 

public: 
    tobj(); 
}; 

class sapp : public QObject 
{ 
    Q_OBJECT 

public: 
    static tobj* f(); 
}; 


void tobj::timeout() 
{ 
    qDebug() << "hi"; 
} 

tobj::tobj() 
{ 
    connect(&timer, SIGNAL(timeout()), this, SLOT(timeout())); 
    timer.setInterval(500); 
    qDebug() << QMetaObject::invokeMethod(&timer, "start", Qt::QueuedConnection); // returns true, but never invoked. 
} 

tobj* sapp::f() 
{ 
    static tobj ff; 
    return &ff; 
} 

ここで私はQtの4.8.0とMSVC 2010年にテストしています1つのヘッダーから成るテストプロジェクトへのリンク、および1つのcppファイルhttp://dl.dropbox.com/u/3055964/untitled.zip

ありがとうございました。ご協力いただきありがとうございます。

答えて

4

あなたはそれを過度にしていると思います。 Qtの美しさは、あなたがやろうとしていることはとても簡単だという事実にあります。

あなたは、QTimerが何らかの形で実行中のスレッドを中断してコールバックを実行すると思うようです。これはQtでは決してありません。 Qtでは、すべてのイベントとシグナルがスレッド内で実行されているイベントループに渡され、そのイベントループによってQtにディスパッチされます。したがって、スレッド内では、Qtのイベントとシグナル/スロットのフレームワークに起因する同時性の危険はありません。また、Qtのシグナルスロットとイベントメカニズムをスレッド間でやりとりすることが必要な場合は、各スレッド内で他のアクセス制御プリミティブを使用する必要はありません。

スレッド内でイベントループを実行することはないため、タイムアウトイベントは受信されず、信号に接続したスロットにディスパッチされません。

QTimerは、起動するスレッドがタイマーQObjectが存在するスレッドであれば、どのスレッドでも作成して開始できます.QObjectは、作成したスレッドに属します。別のスレッドQObject::moveToThread(QThread*)を使用しています。

invokeMethodを使用してタイマーを開始するのは、まったく必要ありません。結局のところ、それが住んでいる同じスレッドからタイマーを開始しています。キューイングされたシグナルスロット接続は、その名前が示すように、キューにシグナルをキューイングします。具体的には、信号を送信すると、QMetaCallEventが受信側スロットが存在するQObjectのキューに入れられます。これを選択して呼び出しを実行するには、イベントループをスロットオブジェクトのスレッドで実行する必要があります。あなたはそのキューを空にするためにスレッド内で何も呼び出さないので、あなたのtimeout()スロットを呼び出すことは何もありません。

静的メンバーの初期化の失敗については、Tオブジェクト全体をmain()またはメインスレッドに存在するQObject内に構築してから、新しいスレッドに移動することができますそれはQtConcurrentを使用しないときです。QtConcurrentを使用する場合、実行可能な関数は任意の数のQObjectを構築および破棄できます。あなたのコードを修正するには

、ラムダは、このように、イベントループを回す必要があります。私の下

[]() { 
    sapp s; 
    s.f(); 
    QEventLoop l; 
    l.exec(); 
} 

を、それは慣用的にQtの中で行われることと思いますかのSSCCE例を添付。 QtConcurrentを使用したくない場合は、exit()のスレッドのイベントループの直後にqApp->exit()にしたい、そうでなければa.exec()は決して終了しません(どのように知っていますか?)。 main()関数を終了する前に、スレッド上でwait()を使用したい場合は、まだ実行中のQThreadsを破棄するのは悪い方法です。

exit()関数は、イベントループのみが完了するためにイベントループを通知します。実際には何も終了していないフラグを設定すると考えられます。したがって、CスタイルAPI exit()sとは異なります。

QTimerは、それ自体がQObjectであることに注意してください。パフォーマンス上の理由から、タイマーが頻繁に起動する場合、QObject::startTimer()によって返されるタイマーIDの周りの単純なラッパーであるQBasicTimerを使用する方がはるかに安いです。その後、QObject::timerEvent()を再実装します。シグナルスロットコールのオーバーヘッドをそのように回避します。経験則として、ダイレクト(非キューイング)シグナルスロットコールは、2つの1000文字のQStringを連結するだけのコストです。 my benchmarkを参照してください。

サイドノート:短い例を投稿している場合は、すべてのコードを1つのファイルに保存している限り、コード全体を直接書き込む方が簡単なので、後で失われることはありません。 3つのファイルに100行のサンプルコードが散在することは非生産的です。以下を参照してください、filename.cppの最後に鍵は#include "filename.moc"です。また、Javaスタイルのメソッドを一度に定義して宣言することもできます。すべてを短くて簡単に続けるという名のもとに。結局、それは一例です。

//main.cpp 
#include <QtCore/QTimer> 
#include <QtCore/QDebug> 
#include <QtCore> 
#include <QtCore/QCoreApplication> 

class Class : public QObject 
{ 
    Q_OBJECT 
    QTimer timer; 
    int n; 
private slots: 
    void timeout() { 
     qDebug() << "hi"; 
     if (! --n) { 
      QThread::currentThread()->exit(); 
     } 
    } 
public: 
    Class() : n(5) { 
     connect(&timer, SIGNAL(timeout()), SLOT(timeout())); 
     timer.start(500); 
    } 
}; 

void fun() 
{ 
    Class c; 
    QEventLoop loop; 
    loop.exec(); 
    qApp->exit(); 
} 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    QtConcurrent::run(&fun); 
    return a.exec(); 
} 

#include "main.moc" 
関連する問題