2015-07-11 15 views
12

Muhtemelen başlık sorusu çok açık değildir. Windows7'de Qt5 kullanıyorum.Qt5: Bir iş parçacığında nasıl bir sinyal beklemeli?

Bir noktada (QThread) bir noktada "process()" işlev/yönteminde, bu iş parçacığı için kullanıyorum QSslSocket'a ait "encrypted()" SIGNAL için beklemeliyim.

// start processing data 
void Worker::process() 
{ 
    status = 0; 
    connect(sslSocket, SIGNAL(encrypted()), this, SLOT(encryptionStarted())); 
    QTimer timer; 
    connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout())); 
    timer.start(10000); 
    while(status == 0) 
    { 
     QThread::msleep(5); 
    } 

    qDebug("Ok, exited loop!"); 

    // other_things here 
    // ................. 
    // end other_things 

    emit finished(); 
} 

// slot (for timer) 
void Worker::timerTimeout() 
{ 
    status = 1; 
} 

// slot (for SSL socket encryption ready) 
void Worker::encryptionStarted() 
{ 
    status = 2; 
} 

Belli ki çalışmıyor: Ayrıca ben QTimer kullanabilir ve
Şimdi ne var olduğunu ... sonsuz döngüye bloke önlemek amacıyla bir "timeout()" SİNYALİ için beklemesi gereken varsayalım . Sonsuza kadar bu süre içinde kalır ...
Yani, soru şu: Bu sorunu çözmenin bir yolu var mı? Bu bekleyen bekle/iş parçacığına takılmamak için, "encrypted()" SİNYAL için ne kadar bekleyeyim - en fazla 10 saniye geçelim?

cevap

21

Sen sinyal beklemek yerel bir olay döngü kullanabilirsiniz yayılan edilecek:

QTimer timer; 
timer.setSingleShot(true); 
QEventLoop loop; 
connect(sslSocket, SIGNAL(encrypted()), &loop, SLOT(quit())); 
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); 
timer.start(msTimeout); 
loop.exec(); 

if(timer.isActive()) 
    qDebug("encrypted"); 
else 
    qDebug("timeout"); 

İşte encrypted duyulur veya zaman aşımı ulaşıncaya kadar bekler.

+0

Çok iyi bir fikir paylaşmaya karar verdik. Test ettim (koduma ve amacımla uyumlu bazı değişikliklerle) ve işe yarıyor. Tabii ki oy kullandım :) –

4

Senkronize olmayan programlamada, "wait for", bir anti-pattern olarak kabul edilir. Bir şeyleri beklemek yerine, yerine getirilmek üzere bir koşula tepki vermek için kodu tasarlayın. Örneğin, kodu bir sinyale bağlayın.

Bunu gerçekleştirmenin bir yolu, eylemlerinizi ayrı durumlara bölmek ve her bir durum girildiğinde bazı işlemler yapmaktır. Tabii ki iş miktarı önemsiz ise, bir şeyler okunabilir tutmak için lambda yerine ayrı bir yuva kullanın.

Açık bellek yönetiminin olmadığını unutmayın. Qt sınıflarına sahip olmak için işaretçi kullanma, zamanından önce bir optimizasyon ve gereksiz yere kaçınılmalıdır. Nesneler, Worker'un (veya onun PIMPL) doğrudan üyeleri olabilir.

Alt nesneler, tümünde Worker kökünde sahiplik hiyerarşisinin bir parçası olmalıdır. Bu şekilde, Worker örneğini başka bir iş parçacığına güvenli bir şekilde taşıyabilirsiniz ve kullandığı nesneler onu izleyecektir. Tabii ki Worker'u doğru iş parçacığına da uygulayabilirsiniz - bunun için basit bir idiom var. İş parçacığının olay çalışanının işçiye sahip olması, böylece iş parçacığı olay döngüsü sonlandırıldığında (yani, QThread::quit() numaralı telefonu çağırdıktan sonra), çalışan otomatik olarak elden çıkarılır ve hiçbir kaynak sızmaz.

template <typename Obj> 
void instantiateInThread(QThread * thread) { 
    Q_ASSERT(thread); 
    QObject * dispatcher = thread->eventDispatcher(); 
    Q_ASSERT(dispatcher); // the thread must have an event loop 
    QTimer::singleShot(0, dispatcher, [dispatcher](){ 
    // this happens in the given thread 
    new Obj(dispatcher); 
    }); 
} 

İşçi uygulaması: Sonra

class Worker : public QObject { 
    Q_OBJECT 
    QSslSocket sslSocket; 
    QTimer timer; 
    QStateMachine machine; 
    QState s1, s2, s3; 
    Q_SIGNAL void finished(); 
public: 
    explicit Worker(QObject * parent = {}) : QObject(parent), 
    sslSocket(this), timer(this), machine(this), 
    s1(&machine), s2(&machine), s3(&machine) { 
    timer.setSingleShot(true); 
    s1.addTransition(&sslSocket, SIGNAL(encrypted()), &s2); 
    s1.addTransition(&timer, SIGNAL(timeout()), &s3); 
    connect(&s1, &QState::entered, [this]{ 
     // connect the socket here 
     ... 
     timer.start(10000); 
    }); 
    connect(&s2, &QState::entered, [this]{ 
     // other_things here 
     ... 
     // end other_things 
     emit finished(); 
    }); 
    machine.setInitialState(&s1); 
    machine.start(); 
    } 
}; 

: QThread::started() bağlanırken açık saçık olacağını

void waitForEventDispatcher(QThread * thread) { 
    while (thread->isRunning() && !thread->eventDispatcher()) 
    QThread::yieldCurrentThread(); 
} 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    struct _ : QThread { ~Thread() { quit(); wait(); } thread; 
    thread.start(); 
    waitForEventDispatcher(&thread); 
    instantiateInThread<Worker>(&myThread); 
    ... 
    return app.exec(); 
} 

Not: Etkinlik memuru QThread::run() içinde bazı kod kadar yok yürütme şansı vardı. Bu nedenle, iş parçacığının üreterek oraya ulaşmasını beklemek zorundayız - bu, çalışan iş parçacığının bir ya da iki verim içinde yeterince ilerlemesi için büyük bir olasılıktır. Böylece zaman kaybetmeyecek.

+0

Çok ilginç bir yaklaşım! Yarın bunu test edeceğim (koduma uyacak şekilde uyarladıktan sonra) ve ilgili geri bildirimi sağlamaya çalışacağım :) Hmmm, bir iş parçacığı içinde bir durum-makinesine sahip olmak harika! Üzgünüm, çok hevesliyim, ama ben QT'de acem oldum, her seferinde yeni bir şey gördüğümde (benim için) Qt/C++'un ne kadar büyük olasılıklar sunabileceğinin farkına varıyorum! Bunun gibi daha fazla örnek görebilseydim! Ne söyleyebilirim? Bravo, mükemmel fikir! –

+0

biraz daha bu konuda genişletmek Can: "açık bellek yönetimi bulunmaması dikkat Kullan Qt sınıflara işaretçileri sahip bir prematüre optimizasyon ve nerede gereksiz kaçınılmalıdır." – Mitch

+1

@Mitch Manüel bellek yönetimi bir şey '' '' '' '' bir 'ham pointer işaretçisine atar ve daha sonra el ile silmek zorundadır. Kodda * yeni * ile * açıkça bellek ayırma yok *, ancak eğer onlar vardıysa, sonuçlar akıllı göstericilere atandı ya da bir QObject ağacında çocuk yaptı. Bir "QObject" in diğer "QObject" ler için akıllı bir işaretçi koleksiyonu olarak çalıştığını unutmayın. –

3

Bu günlerde biraz zaman geçirdim ve biraz araştırma yaptım ..., :(
Kesinlikle maalesef bazı gözlük (satın almanız gerekir ... Daha önce fark etmedi benim gerçek utanç için

bool QSslSocket::waitForEncrypted(int msecs = 30000) 

:
Eh, "http://doc.qt.io/qt-5/qsslsocket.html" göz ve bunu buldum bu bir şaka değil!)
Test etmek için kodumu buna göre değiştirmeye istekliyim (Pazartesi @ ofisi üzerinde)
Çalışacağına dair oldukça fazla şans. Ne dersiniz?
Evet, kendi sorumu cevaplamak biraz garip, ama belki bir çözüm, s o ben :)

İlgili konular