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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Вызов сигнала в главном потоке  (Прочитано 9696 раз)
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« : Август 15, 2012, 14:37 »

Всем привет.
Есть поток, который осуществляет поиск подстроки в нескольких документах.

Поток:
Код:
class SearchThread : public QThread {
Q_OBJECT
public:
QRegExp regexp;
QList<Document*> list;

protected:
void run() {
while (true) {
if (list.isEmpty()) {
break;
}
Document* d = list.takeFirst();

int pos = 0;
while (pos != -1) {
pos = regexp.indexIn(d->Text(), pos);
if (pos >= 0) {
emit Result(SearchResult(d, pos));
}
}
}
}

public:
SearchThread(QObject* parent) {};

signals:
void Result(SearchResult);
};

Результат поиска:
Код:
class SearchResult {
public:
SearchResult(Document*, int) {...}
Document* d;
int pos p;
};

Посредник:
Код:
class Mediator : public QObject {
Q_OBJECT
private:
SearchThread thread;

public:
Mediator() {
connect(&thread, SIGNAL(Result(SearchResult)), this, SIGNAL(Result(SearchResult)), Qt::DirectConnection);
}

void StartSearch() {thread.start();}

signals:
void Result(SearchResult);
};

Мне нужно чтобы сигнал Mediator::Result выполнялся в главном потоке. К этому сигналу подключаются другие объекты и выполняют операции с гуи, что должно быть в главном потоке, но сигнал вызывается из потока поиска. Плюс к этому соединение потока поиска и посредника должно быть не через очередь, а прямое, иначе документ может быть удален после вызова сигнала, но до того, как дошла очередь до обработки этого сигнала, и указатель на документ станет невалидным.
Как можно вызвать Mediator::Result в основном потоке или как-то по другому решить эту проблему?
« Последнее редактирование: Август 15, 2012, 17:51 от Swa » Записан
Bepec
Гость
« Ответ #1 : Август 15, 2012, 15:14 »

Ответ: Никак. Прямое соединение подразумевает вызов в том потоке, который вызывает.

Соединение через очередь вызывается в том потоке, в который послали.

PS если у тебя такая архитектура, что "документ может быть закрыт", то почему бы не сделать "невозможно закрыть документ, пока не обработан сигнал"?
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #2 : Август 15, 2012, 15:31 »

А как узнать, что сигнал не обработан? Ведь нет прямого доступа к очереди событий.
Можно конечно блокировать возможность закрывать документы пока идет поиск но это как-то некрасиво.
Записан
Bepec
Гость
« Ответ #3 : Август 15, 2012, 15:35 »

А что тебе мешает использовать очереди?

Мне немного трудно по коду понять что и как у тебя организовано. Мб опишешь свою идею в словах, а я подскажу как можно сделать/улучшить твою архитектуру?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Август 15, 2012, 15:37 »

Можно так
Код
C++ (Qt)
while (true) {
if (list.isEmpty()) {
               emit Result(0, 0);
break;
}
 
Когда главная нитка видит Result с документом 0, она понимает что поиск завершен и документы можно удалять, и делать search.wait(). А QueuedConnection использовать придется т.к SearchThread выполняется асинхронно
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #5 : Август 15, 2012, 15:56 »

Bepec
Да ничего сложного: список по тексту документов. Юзер запускает поиск и продолжает дальше работать с документами, создавать, удалять и т.д. Вопрос в синхронизации.

Igors
Ну да, это получается единственный вариант.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Август 15, 2012, 16:33 »

Да ничего сложного: список по тексту документов. Юзер запускает поиск и продолжает дальше работать с документами, создавать, удалять и т.д. Вопрос в синхронизации.
Тогда надо лочить takeAt и делать поиск в слоте а не в run
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #7 : Август 15, 2012, 17:51 »

Вообщем, я тут немного подумал и сформулировал проблему немного точнее. В нулевом посте я как-то невнятно объяснил.
На самом деле в потоке стоят мютексы, просто я их опустил ради читабельности. Поток должен реагировать на удаление документа, но ниже описана ситуация, которую я не могу понять как решить.
Код:
class SearchThread : public QThread {
Q_OBJECT
public:
QRegExp regexp;
QList<Document*> list;
QMutex m;

protected:
void run() {
while (true) {
m.lock();
if (list.isEmpty()) {
m.unlock();
break;
}
Document* d = list.takeFirst();

int pos = 0;
while (pos != -1) {
pos = regexp.indexIn(d->Text(), pos);
if (pos >= 0) {
emit Result(SearchResult(d, pos));
}
}
m.unlock();
}
}

public:
SearchThread(QObject* parent) {};

signals:
void Result(SearchResult);
public slots:
void DeletingDocument(Document* d) {
m.lock();
if (list.contains(d)) {list.removeAll(d);}
m.unlock();
}
};

Теперь такая ситуация:
1. SearchThread лочит мютекс и приступает к поиску по конкретному документу d.
2. В это время в основном потоке юзер нажимает на кнопку "Удалить документ d". Основной поток приходит в слот DeletingDocument и ждет мютекса.
3. Поток поиска отсылает сигналы с результатом поиска, заканчивает работать с документом d и открывает мютекс. Сигналы отправляются в очередь событий основного потока.
4. Основной поток пытается удалить документ d из списка, но там его нет. Он выходит из слота и освобождает память документа d.
5. После этого основной поток начинает обрабатывать данные сигналов Result, которые уже содержат невалидные указатели.

Как обработать эту ситуацию?
Записан
Bepec
Гость
« Ответ #8 : Август 15, 2012, 18:03 »

При нажатии кнопачки удалить документ проверять, идёт ли по нему поиск. Если да, то выдавать окно - аля "в данном документе производится поиск. При удалении документа поиск будет отменён" да/нет. Ну а если да, тогда в поток поиска отсылать сигнал - начать экстренную панику и прекращение потока поиска.

PS и овцы целы и волков постреляли.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 15, 2012, 19:23 »

Так просто не выйдет, надо переделывать эту, как ее... "архитектуру" Улыбающийся

Проще всего перенести поиск из run в слот. Главная нитка пуляет в этот слот документом для поиска. Удаляет тоже главная, так легче. SearchThread стоит, получмл документ, делает поиск, дальше стоит. Плюс у него стандартный флажок mAbort чтобы прекратить поиск (документ будет удален). Ну это жевали здесь многократно.

Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #10 : Август 16, 2012, 08:35 »

Igors
Я не совсем понимаю вашу мысль. Поиск производится сразу по всем документам. Если главная нитка будет перебирать все документы и посылать их в SearchThread, то она не сможет реагировать на действия юзера.

Цитировать
Ну это жевали здесь многократно.
А можно ссылку на похожую тему?
Записан
Bepec
Гость
« Ответ #11 : Август 16, 2012, 09:03 »

Поиск "сразу по всем" - плохая идея Улыбающийся

К тому же у тебя идёт поиск последовательно, исходя из кода.

У тебя итак сейчас идёт переборчик, правда из кода не очень понятно как туда они попадают.

PS и уж точно, работа с <100 указателями в главной нитке ничем её не затормозит. Вообще Улыбающийся
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #12 : Август 16, 2012, 09:23 »

Сначала было так:
void Widget::SearchButtonPressed() {
  thread->list = this->list;
  thread.start();
}

Но Igors советует отдавать в поток один документ:
void Widget::SearchButtonPressed() {
  foreach(Document* d, list) {
    thread->document = d;
    thread.start();
    thread.wait();
  }  <- главный поток ждет конца поиска
}

А почему поиск по всем - плохая идея?
« Последнее редактирование: Август 16, 2012, 09:26 от Swa » Записан
Bepec
Гость
« Ответ #13 : Август 16, 2012, 09:39 »

Напиши, хотя бы псевдокодом,  как ты будешь искать одновременно (не последовательно) по всем документам?

PS это возможно только когда на каждый документ будет 1 поток Веселый А >8 потоков в программе - плохо Веселый
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #14 : Август 16, 2012, 09:55 »

Наверное мы друг друга не поняли Улыбающийся. Поиск, естественно, идёт последовательно. Код перебора - в предыдущем ответе.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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