В моей программе я создаю подкласс QThread, и я реализовал виртуальный метод run()
примерно так:
void ManagerThread::run() {
// do a bunch of stuff,
// create some objects that should be handled by this thread
// connect a few signals/slots on the objects using QueuedConnection
this->exec(); // start event loop
}
Теперь, в другом потоке (назовем его MainThread
), я запускаюManagerThread
и ждут его started()
сигнала , после чего я перехожу к использованию сигналов и слотов, которые должны обрабатываться ManagerThread
.Однако сигнал started()
по существу излучается непосредственно перед вызовом run()
, поэтому в зависимости от планирования потоков я теряю некоторые сигналы из MainThread
, поскольку цикл обработки событий еще не начался!
( РЕДАКТИРОВАТЬ: оказывается, что это не проблема, просто сигналы не связаны во времени, но по той же причине)
Я мог бы испустить сигнал прямо перед вызовом exec()
, но этотакже просят проблемы.
Есть ли какой-то определенный / простой способ узнать, что цикл событий начался?
Спасибо!
EDIT2: (РЕШЕНИЕ)
Хорошо, так получается, что проблема не совсем в том, что я сформулировал.Тот факт, что цикл обработки событий не начался, не является проблемой, поскольку сигналы должны ставиться в очередь до тех пор, пока он не запустится.Проблема в том, что некоторые сигналы не будут подключены вовремя для вызова, поскольку сигнал started()
испускается до вызова run()
.
Решение состоит в для передачи другого пользовательского сигнала после всех подключений и непосредственно перед exec.Таким образом гарантируется подключение всех сигналов / слотов.
Это решение проблемы my , но на самом деле это не ответ на заголовок темы.Я принял ответ, что отвечает на заголовок.
Я оставил весь свой код ниже для тех, кому любопытно, с решением, ждать другого сигнала в методе instance()
.
КОД:
Многие из вас говорят, что я не могу потерять сигналы, так что вот моя реализация всего класса.Я упросту его до самого необходимого.
Вот интерфейс ManagerThread
:
// singleton class
class ManagerThread: public QThread {
Q_OBJECT
// trivial private constructor/destructor
public:
static ManagerThread* instance();
// called from another thread
public:
void doSomething(QString const& text);
// emitted by doSomething,
// connected to JobHandler whose affinity is this thread.
signals:
void requestSomething(QString const& text);
// reimplemented virtual functions of QThread
public:
void run();
private:
static QMutex s_creationMutex;
static ManagerThread* s_instance;
JobHandler* m_handler; // actually handles the requests
};
Некоторые соответствующие реализации.Создание единственного экземпляра потока:
ManagerThread* ManagerThread::instance() {
QMutexLocker locker(&s_creationMutex);
if (!s_instance) {
// start socket manager thread, and wait for it to finish starting
s_instance = new ManagerThread();
// SignalWaiter essentially does what is outlined here:
// /3234007/v-ozhidanii-signala
SignalWaiter waiter(s_instance, SIGNAL(started()));
s_instance->start(QThread::LowPriority);
qDebug() << "Waiting for ManagerThread to start";
waiter.wait();
qDebug() << "Finished waiting for ManagerThread thread to start.";
}
return s_instance;
}
Повторная реализация прогона, который устанавливает сигналы / слоты и запускает цикл обработки событий:
void ManagerThread::run() {
// we are now in the ManagerThread thread, so create the handler
m_handler = new JobHandler();
// connect signals/slots
QObject::connect(this,
SIGNAL(requestSomething(QString const&)),
m_handler,
SLOT(handleSomething(QString const&)),
Qt::QueuedConnection);
qDebug() << "Starting Event Loop in ManagerThread";
// SOLUTION: Emit signal here and wait for this one instead of started()
this->exec(); // start event loop
}
Функция, которая делегирует обработку правильному потоку.Вот где я посылаю потерянный сигнал:
void ManagerThread::doSomething(QString const& text) {
qDebug() << "ManagerThread attempting to do something";
// if calling from another thread, have to emit signal
if (QThread::currentThread() != this) {
// I put this sleep here to demonstrate the problem
// If it is removed there is a large chance the event loop
// will not start up in time to handle the subsequent signal
QThread::msleep(2000);
emit(requestSomething(text));
} else {
// just call directly if we are already in the correct thread
m_handler->handleSomething(text);
}
}
Наконец, вот код из MainThread
, который завершится ошибкой, если цикл событий не запустится вовремя:
ManagerThread::instance()->doSomething("BLAM!");
Предполагая, что обработчик просто распечатывает свой текст, вот что распечатывается при успешном запуске:
Ожидание запуска ManagerThread
Завершено ожидание запуска потока ManagerThread.
Запуск цикла событий в ManagerThread
ManagerThread пытается что-то сделать
BLAM!
И вот что происходит при неудачном запуске:
ОжиданиеManagerThread для запуска
Завершено ожидание запуска потока ManagerThread.
ManagerThread пытается что-то сделать
Запуск цикла событий в ManagerThread
Ясно, что цикл событий начался после того, как сигнал был отправлен,и БЛЕМ никогда не печатает.Здесь есть условие гонки, которое требует знания того, когда начинается цикл событий, чтобы исправить это.
Может быть, я что-то упускаю, а проблема в чем-то другом ...
Большое спасибо, если вы все это прочитали!Уф!