2017-03-02 7 views
5

は基本的に私はJavaで次のように行われている同じことをする必要があります。したがって、いくつかの擬似コード:ラムダ関数をQtのイベントループに入れる方法は? <pre><code>SwingUtilities.invokeLater(()->{/* function */}); </code></pre> <p>またはこのような</p> JavaScriptで:</p> <pre><code>setTimeout(()=>{/* function */}, 0); </code></pre> <p>しかし、Qtとラムダとの

Qt::queuePushMagic([]() { /* function */ }); 

さらに複雑な問題として、私はマルチスレッド環境で動作するためにこれを必要とします。私が実際にやっていることは、正しいスレッドで特定のメソッドを自動的に実行することです。どのようなコードが表示されますか:

SomeClass::threadSafeAsyncMethod() { 
    if(this->thread() != QThread::currentThread()) { 
     Qt::queuePushMagic([this]()=>{ this->threadSafeAsyncMethod() }); 
     return; 
    } 
} 

これを行う方法は?

+0

あなたは 'QTimer'を試しましたか? – MrEricSir

+0

それは私が推測することができます。しかし、言及すべき重要な点と重要な点の一つは、**他のスレッドの**イベントループを押し込もうとしていることです。 –

答えて

11

問題はHow to leverage Qt to make a QObject method thread-safe?です。そこで提供されているソリューションをあなたのユースケースに適応させましょう。まずは、安全チェックを考慮してみましょう:

bool isSafe(QObject * obj) { 
    Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread()); 
    auto thread = obj->thread() ? obj->thread() : qApp->thread(); 
    return thread == QThread::currentThread(); 
} 

あなたが提案したアプローチがファンクタを取り、ファンクタ内の引数(もしあれば)を荷造りして、コンパイラの契約をすることができます:

template <typename Fun> void postCall(QObject * obj, Fun && fun) { 
    qDebug() << __FUNCTION__; 
    struct Event : public QEvent { 
     using F = typename std::decay<Fun>::type; 
     F fun; 
     Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {} 
     Event(const F & fun) : QEvent(QEvent::None), fun(fun) {} 
     ~Event() { fun(); } 
    }; 
    QCoreApplication::postEvent(
      obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun))); 
} 

A第2のアプローチは、明示的にイベント内のすべてのパラメータのコピーを格納し、ファンクタを使用していない次のよう

template <typename Class, typename... Args> 
struct CallEvent : public QEvent { 
    // See https://stackoverflow.com/a/7858971/1329652 
    // See also https://stackoverflow.com/a/15338881/1329652 
    template <int ...> struct seq {}; 
    template <int N, int... S> struct gens { using type = typename gens<N-1, N-1, S...>::type; }; 
    template <int ...S>  struct gens<0, S...> { using type = seq<S...>; }; 
    template <int ...S>  void callFunc(seq<S...>) { (obj->*method)(std::get<S>(args)...); } 
    Class * obj; 
    void (Class::*method)(Args...); 
    std::tuple<typename std::decay<Args>::type...> args; 
    CallEvent(Class * obj, void (Class::*method)(Args...), Args&&... args) : 
     QEvent(QEvent::None), obj(obj), method(method), args(std::move<Args>(args)...) {} 
    ~CallEvent() { callFunc(typename gens<sizeof...(Args)>::type()); } 
}; 

template <typename Class, typename... Args> void postCall(Class * obj, void (Class::*method)(Args...), Args&& ...args) { 
    qDebug() << __FUNCTION__; 
    QCoreApplication::postEvent(
      obj->thread() ? static_cast<QObject*>(obj) : qApp, new CallEvent<Class, Args...>{obj, method, std::forward<Args>(args)...}); 
} 

それが使われています:

struct Class : QObject { 
    int num{}; 
    QString str; 
    void method1(int val) { 
     if (!isSafe(this)) 
     return postCall(this, [=]{ method1(val); }); 
     qDebug() << __FUNCTION__; 
     num = val; 
    } 
    void method2(const QString &val) { 
     if (!isSafe(this)) 
     return postCall(this, &Class::method2, val); 
     qDebug() << __FUNCTION__; 
     str = val; 
    } 
}; 

テストハーネス:

// https://github.com/KubaO/stackoverflown/tree/master/questions/safe-method-40382820 
#include <QtCore> 

// above code 

class Thread : public QThread { 
public: 
    Thread(QObject * parent = nullptr) : QThread(parent) {} 
    ~Thread() { quit(); wait(); } 
}; 

void moveToOwnThread(QObject * obj) { 
    Q_ASSERT(obj->thread() == QThread::currentThread()); 
    auto thread = new Thread{obj}; 
    thread->start(); 
    obj->moveToThread(thread); 
} 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    Class c; 
    moveToOwnThread(&c); 

    const auto num = 44; 
    const auto str = QString::fromLatin1("Foo"); 
    c.method1(num); 
    c.method2(str); 
    postCall(&c, [&]{ c.thread()->quit(); }); 
    c.thread()->wait(); 
    Q_ASSERT(c.num == num && c.str == str); 
} 

出力:

postCall 
postCall 
postCall 
method1 
method2 

上記のコンパイルとQt 4またはQtのいずれかで動作5.

は、様々な方法を模索し、またthis questionを見ますQtの他のスレッドコンテキストでFunctorを呼び出す

+0

これは素晴らしいですね。私はすでに他の提案と一緒に遊んでいて、もう1つの問題を見つけました。スレッドが*まだ*実行されていない場合はどうなりますか?私のデザインでは問題かもしれません。私は 'QThread :: run'を再実装したバージョンで' this-> moveToThread(this) 'を呼び出しています。この問題が関連しているかどうか、あるいは私が間違っているだけなのかどうかはわかりません。 –

+2

特定のオブジェクトにポストされたイベントは、スレッドを切り替えるときにオブジェクトを追跡するため、問題はありません。しかし、 'this-> moveToThread(this)'は反パターンです。 'QThread'に触れる必要はありません。あなたのコードを 'QObject'に入れて、すでに実行されているスレッドに移動してください。 –

+0

私はQThreadサブクラスを維持して、それらが表すスレッドで動作するとは思わないでしょうか?私はそれがそのように動作すると思った。私はもちろんスレッドを別々にすることもできますが、それはちょっとしたコードだと思っていました。 –

関連する問題