Russian Qt Forum
Ноябрь 24, 2024, 03:05 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QThread - синхронизация с главным потоком.  (Прочитано 13593 раз)
spectre71
Гость
« : Июль 15, 2009, 13:24 »

Задача.
1) Имеем объект некоторого класса, имеем метод который может быть вызван в любом потоке(в зависимости от ситуации), как в главном, так и в вспомогательном.
2) Выполнение данного метода может быть очень длительным и как правило для его выполненния создается вспомогательный поток
3) В данном методе есть вызов другого метода в котором происходит создание некоторого QWidget
4) Проблема в том что QWidget могут создаваться только в главном потоке.

Как сделать внутри второстепенного потока вызов метода в контексте главного потока?
В билдере у класса TThread для этих целей был "syncronize(method)", есть ли что-либо подобное в QT, а если нет, то как решить проблему?
Записан
ритт
Гость
« Ответ #1 : Июль 15, 2009, 17:15 »

в контексте QApplication или TL Widget приписать слот, создающий необходимый виджет. из п.3 асинхронно вызывать данный слот.

насколько я помню, багландовский syncronize замораживает задействованные потоки...
Записан
alexman
Гость
« Ответ #2 : Июль 15, 2009, 22:13 »

Например, можно послать сигнал из вспомогательного потока главному на создание QWidget и уснуть на QWaitCondition (метод bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX )). Далее в главном окне что-то делаем с QWidget, а по завершению вызываем метод второстепенного потока из главного, в котором делаем wakeOne.
Записан
spectre71
Гость
« Ответ #3 : Июль 16, 2009, 11:48 »

Спасибо Константин и alexman.
Вроде работает. Если есть желание, посмотрите что получилось, может что еще поскажете или найдете ошибки.
синхронизация делается через наследника SbThreadSync, вызовом статического метода SbThread::synchronize(SbThreadSync* s);
Еще сделан #define sb_synchronize_method. Можно делать синхронизацию так:

Код
C++ (Qt)
void MyClass::methodForSync(void) {
 ...
}
 
void MyClass::doAny(void) {
 ...
 ...
 sb_synchronize_method(MyClass, methodForSync);
 ...
 ...
}

Далее исходники
sbw_kernel.h
Код
C++ (Qt)
//---------------------------------------------------------------------------
 
#ifndef sbw_kernelH
#define sbw_kernelH
 
//---------------------------------------------------------------------------
 
#include <QObject>
#include <QApplication>
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
 
/*-----------------------------------------------------------------------------*/
 
class SbThread;
class SbThreadSync;
class SbApplication;
 
/*-----------------------------------------------------------------------------*/
 
class SbThread : public QThread {
   Q_OBJECT
 protected:
   QAtomicInt Stopped;
   QMutex Mutex;
   QWaitCondition Wait;
   SbApplication* Application;
 public:
   SbThread(bool FreeOnComplete = false, QObject * parent = 0);
   virtual ~SbThread();
 
   inline bool stopped (void) const {return Stopped;}
   inline void stop    (void) {Stopped.ref();}
   void wake (void);
 
 public:
   static void synchronize(SbThreadSync* s);
 
 protected:
   void doSynchronize(SbThreadSync* s);
 
 signals:
   void signalSynchronize(SbThreadSync* s);
 
};
 
/*-----------------------------------------------------------------------------*/
 
class SbThreadSync {
   friend class SbThread;
   friend class SbApplication;
 private:
   SbThread* Thread;
 protected:
   void syncCall(void);
 public:
   SbThreadSync() {Thread = NULL;}
   virtual ~SbThreadSync() {}
 
   virtual void call(void) = 0;
};
 
#define sb_synchronize_method(__type, __method) \
 { \
   class TrSync : public SbThreadSync { \
     private : \
       __type* obj; \
       void (__type::*method)(void); \
     public : \
       TrSync(__type* obj, void (__type::*method)(void)): SbThreadSync() {this->obj = obj; this->method = method;} \
       void call (void) {(obj->*method)();} \
   } trs(this, &__type::__method); \
   SbThread::synchronize(&trs); \
 }
 
/*-----------------------------------------------------------------------------*/
 
class SbApplication : public QApplication {
   Q_OBJECT
 public:
   static QThread* thread(void) {return QApplication::instance()->thread();}
 public:
   SbApplication(int& argc, char** argv) : QApplication(argc, argv) {}
 
 private slots:
   void synchronize(SbThreadSync* s) {s->syncCall();}
};
 
#endif
 

sbw_kernel.cpp
Код
C++ (Qt)
/*-----------------------------------------------------------------------------*/
 
#include "sbw_kernel.h"
 
/*-----------------------------------------------------------------------------*/
 
/***************************
*     class SbThread
***************************/

 
SbThread::SbThread(bool FreeOnComplete, QObject* parent) : QThread(parent) {
 Stopped = 0;
 if(FreeOnComplete) {connect(this , SIGNAL(finished()), this , SLOT(deleteLater()));}
 SbApplication* Application = qobject_cast<SbApplication*>(QApplication::instance());
 if(Application)    {connect(this , SIGNAL(signalSynchronize(SbThreadSync*)), Application, SLOT(synchronize(SbThreadSync*)), Qt::QueuedConnection);}
}
 
SbThread::~SbThread() {
}
 
void SbThread::wake (void) {
 Wait.wakeOne();
}
 
void SbThread::doSynchronize(SbThreadSync* s) {
 Mutex.lock();
 emit signalSynchronize(s);
 Wait.wait(&Mutex);
 Mutex.unlock();
}
 
void SbThread::synchronize(SbThreadSync* s) {
 if(SbApplication::thread() == QThread::currentThread()) {
   s->call();
   return;
 }
 SbThread* Thread = qobject_cast<SbThread*>(QThread::currentThread());
 if(!Thread || !Thread->Application) {
   s->call(); // OR throw
   return;
 }
 s->Thread = Thread;
 Thread->doSynchronize(s);
}
 
/*-----------------------------------------------------------------------------*/
 
/***************************
*     class SbThreadSync
***************************/

 
void SbThreadSync::syncCall(void) {
 call();
 Thread->wake();
}
 
/*-----------------------------------------------------------------------------*/
 
Записан
Winstrol
Гость
« Ответ #4 : Июль 16, 2009, 16:35 »

  • Например, можно послать сигнал из вспомогательного потока главному на создание QWidget и уснуть на QWaitCondition (метод bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX )). Далее в главном окне что-то делаем с QWidget, а по завершению вызываем метод второстепенного потока из главного, в котором делаем wakeOne.
    • Во-первых, help по Qt::ConnectionType.
    • Во-вторых, низкоуровневые примитивы вроде QMutex,QAtomicInt и.т.п. в прикладном коде следует всячески избегать, т.к. при наличии QtConcurrent они тупо практически никогда не нужны, а если и нужны, то часто есть более лучшие решения без них.
    • В-третьих, что будет
      Поток A шлет сигнал главному потоку сигнал  на создание QWidget и засыпает.
      Поток B шлет сигнал главному потоку сигнал  на создание QWidget и засыпает.
      Главный поток создает QWidget по запросу потока А и делает wakeOne.
      Вопрос? Какой поток проснется A или B.
    Цитировать
    void QWaitCondition::wakeOne ()
    Wakes one thread waiting on the wait condition. The thread that is woken up depends on the operating system's scheduling policies, and cannot be controlled or predicted.
    If you want to wake up a specific thread, the solution is typically to use different wait conditions and have different threads wait on different conditions.
    See also wakeAll().

Записан
spectre71
Гость
« Ответ #5 : Июль 16, 2009, 19:07 »

Во-первых, help по Qt::ConnectionType.
Ну и что? Что плохого в том что я явно указал Qt::QueuedConnection

Во-вторых, низкоуровневые примитивы вроде QMutex,QAtomicInt и.т.п. в прикладном коде следует всячески избегать, т.к. при наличии QtConcurrent они тупо практически никогда не нужны, а если и нужны, то часто есть более лучшие решения без них.
Покажи решение лучше!

В-третьих, что будет
Поток A шлет сигнал главному потоку сигнал  на создание QWidget и засыпает.
Поток B шлет сигнал главному потоку сигнал  на создание QWidget и засыпает.
Главный поток создает QWidget по запросу потока А и делает wakeOne.
Вопрос? Какой поток проснется A или B.
проснется A!
У них разные QWaitCondition, смотри код.
Записан
Winstrol
Гость
« Ответ #6 : Июль 16, 2009, 21:10 »

Ну и что? Что плохого в том что я явно указал Qt::QueuedConnection
help по Qt::ConnectionType
Цитировать
Покажи решение лучше!
help по Qt::ConnectionType
Цитировать
У них разные QWaitCondition, смотри код.
Извиняюсь, не посмотрел. Жесть! Чистая и незамутненная.
Записан
spectre71
Гость
« Ответ #7 : Июль 16, 2009, 21:49 »

Ну и что? Что плохого в том что я явно указал Qt::QueuedConnection
help по Qt::ConnectionType
Цитировать
Покажи решение лучше!
help по Qt::ConnectionType
Цитировать
У них разные QWaitCondition, смотри код.
Извиняюсь, не посмотрел. Жесть! Чистая и незамутненная.
help по Qt::ConnectionType
Непонимающий
Записан
Winstrol
Гость
« Ответ #8 : Июль 16, 2009, 22:15 »

Для тех, у кого устаревший хелп, правильный ответ:
QMetaObject::invokeMethod(&...,"addWidget",Qt::BlockingQueuedConnection);   
или
emit addWidget();
Записан
spectre71
Гость
« Ответ #9 : Июль 16, 2009, 23:22 »

Для тех, у кого устаревший хелп, правильный ответ:
QMetaObject::invokeMethod(&...,"addWidget",Qt::BlockingQueuedConnection);   
или
emit addWidget();
Qt::BlockingQueuedConnection - возможно, только не совсем понятно что значит:
Цитировать
Same as QueuedConnection, except that the current thread blocks until the slot has been delivered.
delivered - "доставлен" или "освобожден" - совершенно разный смысл, и понимай как хочешь! Если именно освобожден, то можно обойтись и без QMutex и QWaitCondition.
А QMetaObject::invokeMethod - не интересно, покольку задача выполнить любой метод/методы (любого объекта  или обычный метод) из дочернего потока в контексте главного.
Записан
ритт
Гость
« Ответ #10 : Июль 16, 2009, 23:31 »

Для тех, у кого устаревший хелп, правильный ответ:
QMetaObject::invokeMethod(&...,"addWidget",Qt::BlockingQueuedConnection);   
или
emit addWidget();
Qt::BlockingQueuedConnection - возможно, только не совсем понятно что значит:
Цитировать
Same as QueuedConnection, except that the current thread blocks until the slot has been delivered.
delivered - "доставлен" или "освобожден" - совершенно разный смысл, и понимай как хочешь! Если именно освобожден, то можно обойтись и без QMutex и QWaitCondition.
доставлен. для блокировки потока используется мутекс (если правильно помню). пока ивент не будет доставлен, мутекс не разблокируется.

А QMetaObject::invokeMethod - не интересно, покольку задача выполнить любой метод/методы (любого объекта  или обычный метод) из дочернего потока в контексте главного.
А QMetaObject::invokeMethod именно это и делает.
Записан
spectre71
Гость
« Ответ #11 : Июль 17, 2009, 09:36 »

доставлен. для блокировки потока используется мутекс (если правильно помню). пока ивент не будет доставлен, мутекс не разблокируется.
Я правильно тебя понял, "доставлен" - момент непосредственно перед вызовом слота.

А QMetaObject::invokeMethod именно это и делает.
Согласен, но в данном случае, что через сигнал, что через него - особой разницы нет.
Записан
ритт
Гость
« Ответ #12 : Июль 17, 2009, 10:11 »

сигнализируя, ты не можешь ожидать момент доставки - поведение зависит целиком от connect
в случае же с invokeMethod ты сам можешь определять как будет вести себя конкретный клочок кода - в одном месте можно дожидаться доставки, в другом - нет...вдобавок для использования не требуется макрос Q_OBJECT и для ряда случаев сигналы вообще.
Записан
Winstrol
Гость
« Ответ #13 : Июль 17, 2009, 10:40 »

доставлен. для блокировки потока используется мутекс (если правильно помню). пока ивент не будет доставлен, мутекс не разблокируется.
Я правильно тебя понял, "доставлен" - момент непосредственно перед вызовом слота.
Нет.

Цитировать
void SbThread::wake (void) {
  Wait.wakeOne();
}
Может получить управление до wait
Записан
spectre71
Гость
« Ответ #14 : Июль 17, 2009, 17:17 »

Может получить управление до wait

Если я привильно понял(QWaitCondition::wait)
Цитировать
This function is provided to allow the atomic transition from the locked state to the wait state.

Тогда так
Код
C++ (Qt)
void SbThread::wake (void) {
 Mutex.lock();
 Wait.wakeOne();
 Mutex.unlock();
}
 
void SbThread::doSynchronize(SbThreadSync* s) {
 Mutex.lock();
 emit signalSynchronize(s);
 Wait.wait(&Mutex);
 Mutex.unlock();
}
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.243 секунд. Запросов: 21.