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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Недопонимаю в потоках  (Прочитано 11550 раз)
crocus
Гость
« : Ноябрь 18, 2006, 05:51 »

В общем смысл в чем- запускается несколько потоков
Код:

void NewspaperWindow::autoThread()
{
QStringList folders;
folders << "0" << "1" << "2" << "3" << "4";
for (int i = 0; i < folders.size(); ++i){
text = folders.at(i).toLocal8Bit().constData();
ThreadProcess *thread = new ThreadProcess(text, this);
thread->start(QThread::HighPriority);
}
}


Это код потока:
Код:

#include <QFile>
#include <QFileInfo>
#include <QModelIndex>
#include <QTextStream>
#include <QTextDocument>
#include <QStringList>
#include <QDir>
#include "threadprocess.h"

ThreadProcess::ThreadProcess(QString flats, QObject *parent)
: QThread(parent), th_flats(flats)
{
}

ThreadProcess::~ThreadProcess()
{}
void ThreadProcess::run()
{
th_model = new QSqlTableModel();
th_model->setTable("Str_rep");
th_model->select();
while ( th_model->canFetchMore() )
th_model->fetchMore();
th_model_phon = new QSqlTableModel();
th_model_phon->setTable("Phon");
th_model_phon->select();
while ( th_model_phon->canFetchMore() )
th_model_phon->fetchMore();
QStringList files;
QString dirname = "C:\\Local Web\\" + th_flats + "\\www.dalpress.ru\\gazeta\\list";
QDir directory = QDir( dirname );
if ( fileName.isEmpty() )
fileName = "*";
files = directory.entryList( QStringList( fileName ),
QDir::Files | QDir::NoSymLinks );
unitName = "uniter" + th_flats + ".txt";
ThreadfindFiles( directory, files );
processText();
threadSave();
}
void ThreadProcess::ThreadfindFiles( const QDir &directory, const QStringList &files)
{
QStringList foundFiles;
QTextStream out(&proc_str);

for ( int i = 0; i < files.count(); ++i )
{
qApp->processEvents();

QFile file( directory.absoluteFilePath( files[ i ] ) );

if ( file.open( QIODevice::ReadOnly ) )
{
QTextStream in(&file);
while ( !in.atEnd() )
{
QByteArray data = file.readAll();
QTextCodec *codec = Qt::codecForHtml( data );
str = codec->toUnicode( data );
doc = new QTextDocument();
if ( Qt::mightBeRichText( str ) )
{
doc->setHtml( str );
str = doc->toPlainText();
}
else
{
str = QString::fromLocal8Bit( data );
    }
out << str;
foundFiles << files[ i ];
}
}
}
}
void ThreadProcess::processText()
{
QString street;
QString street_rep;
for ( int i = 0; i < th_model->rowCount(); ++i )
{
QModelIndex indexStr = th_model->index( i, 0 );
QModelIndex indexRep = th_model->index( i, 1 );
street = th_model->data( indexStr, Qt::DisplayRole ).toString();
street_rep = th_model->data( indexRep, Qt::DisplayRole ).toString();
proc_str.replace( street + " ", "\t" + street_rep + " " );
}

QString phon;
for ( int i = 0; i < th_model_phon->rowCount(); ++i )
{
QModelIndex indexphon = th_model_phon->index( i, 0 );
phon = th_model_phon->data( indexphon ).toString();
proc_str.replace( phon, "" );
}
QString strout;
QTextStream in(&proc_str );
QTextStream out(&strout);
while (!in.atEnd()) {
QString line = in.readLine();
int j = 6 - line.count(QRegExp("\\t"));
for (int i = 0; i < j; ++i) {
line.append("\t");
}
if (line.contains(QRegExp("\\d{5,}"))){
if(!th_flats.isNull()){
out << th_flats << "\t" << line << "\n";
line = th_flats + "\t" + line;
} else {
out  << line << "\n";
}
QStringList list = line.split(QRegExp("\\t"));
[color=red]///// showList(list); // здесь вызывается функция главного окна - а именно заполнение TableWidgeta данными [/color]
}
}
doc->setPlainText(strout);
}
void ThreadProcess::threadSave()
{
QFile file(unitName);
if ( !file.open( QFile::WriteOnly) )
{
return ;
}
QTextStream save_out( &file );
QApplication::setOverrideCursor( Qt::WaitCursor );
save_out << doc->toPlainText();
QApplication::restoreOverrideCursor();
}


В потоке идет построчная обработка текста и каждая строка записывается в TableWidget главного окна.
Так вот не понимаю как корретно и безопасно вернуть данные  list  
Код:
QStringList list = line.split(QRegExp("\\t"));	
[color=red]///// showList(list); // здесь вызывается функция главного окна - а именно заполнение TableWidgeta данными [/color]
}
в гланое окно (главный класс).
Код:
bool NewspaperWindow::showList( const QStringList &list)
{
int j = 0;
listTable->insertRow(j);
for ( int i = 0; i < listTable->columnCount(); ++i )
{
QTableWidgetItem *listNameItem = new QTableWidgetItem( list[ i ] );
listTable->setItemDelegate(new TableDelegate());
listTable->setItem( j, i, listNameItem );
}
return true;
}

Последовательный код работает, выигрыш во времени при работе в потоках около 5 с.
Записан
Tonal
Гость
« Ответ #1 : Ноябрь 18, 2006, 10:24 »

Полож QStringList list в очередь - QQueue.
Пошли в главное окно сигнал.
По приходу сигнала забери из очереди и добавь в таблицу.

Операции положения/забирания над очередью защити QMutex + QMutexLocker.
И будет счастье! ;-)

Да, чтобы совсем не тормозило, имеет смысл в главном окне обрабатывать не по 1 строке за раз а блоками, по 10 - 100 (в зависимости от общего количества)...
Ну и использовать не QTableWidget а QTableView. ;-)
Записан
QCasper
Гость
« Ответ #2 : Ноябрь 18, 2006, 16:25 »

Цитата: "Tonal"
Полож QStringList list в очередь - QQueue.
Операции положения/забирания над очередью защити QMutex + QMutexLocker.


А QQueue разве не потоко-безопасна?
Записан
SLiDER
Гость
« Ответ #3 : Ноябрь 18, 2006, 21:37 »

Цитата: "QCasper"
А QQueue разве не потоко-безопасна?


Ни в коем случае, QQueue это в чистом виде QList со всеми вытекающими ...
Записан
QCasper
Гость
« Ответ #4 : Ноябрь 19, 2006, 05:07 »

Цитата: "SLiDER"
Цитата: "QCasper"
А QQueue разве не потоко-безопасна?


Ни в коем случае, QQueue это в чистом виде QList со всеми вытекающими ...


Я вот как раз таки и думал, что лист безопасен, наверное путаю с STLевским листом...
Записан
SLiDER
Гость
« Ответ #5 : Ноябрь 19, 2006, 17:05 »

Цитата: "QCasper"
Цитата: "SLiDER"
Цитата: "QCasper"
А QQueue разве не потоко-безопасна?


Ни в коем случае, QQueue это в чистом виде QList со всеми вытекающими ...


Я вот как раз таки и думал, что лист безопасен, наверное путаю с STLевским листом...


Опять же, что следует понимать под потокобезопасностью? Как тут недавно говорил Dendy (если мне не изменяет память) и пишут у себя в assistant-е троли, есть два типа потокобезопаности:

1. Reentrancy - когда безопасен вызов разных методов использующих разные данные объекта  из разных потоков.
2. Thread-Safety - когда безопасен вызов методов использующих одни и теже данные или вызов одного и того же метода из разных потоков.

И если первое можно запросто реализовать не прибегая к помощи объектов межпоточной синхронизации, то, во втором случае, без них, ну ни как, не обойтись.

Т.о. со вторым типом все понятно, внедрять объекты синхронизации прямо в класс списка безумное рассточительство вычислительных ресурсов, т.к. вызов блокировки через мутекс по вычислителным затратам на порядки превышает, например, операцию сравнения.  А вот каким образом реализовать операции доступа к данным списка, в стиле первого типа, я честно говоря представить себе не могу, исходя из самого принципа построения списка, т.е. когда добраться до определенного элемента можно только посредством последовательного перебора.

З.Ы. Хотя в последнем случае я уверен не на 100%, возможно Reentrancy для своего QList-а тролям реализовать и удалось. Хотелось бы услышать мнения других экспертов.  Веселый
Записан
Dendy
Гость
« Ответ #6 : Ноябрь 19, 2006, 20:51 »

1. Reentrant method - можно вьІзьІвать сей метод одновременно из разньІх потоков для разньІх екземпляра класса.

2. Thread safe method - можно вьІзьІвать сей метод одновременно из разньІх потоков для одного и того же екземпляра класса.

Потокобезопастность контейнеров заключается в атомарности операций копирования. Можно делать так:

Код:
QList<int> global_shared_list;

// thread 1
global_shared_list << 123;

// thread 2
for ( QListIterator<int> it( global_shared_list ); it.hasNext(); )
  do_something_with_int( it.next() );


В конструкторе итератора создаётся копия списка, ета операция атомарна, реального копирования данньІх не происходит. Если в течении итерации первьІй поток попьІтался изменить контейнер - произойдёт разделение данньІх и второй поток нормально продолжит работать с полученной ранее копией контейнера.

Грубо говоря: логически присваивание контейнеров всегда создаёт независимую копию содержимого.

Конечно же, если вам нужно изменять контейнер из разньІх потоков - такая схема не подойдёт. Ведь вам нужно будет работать не с копиями данньІх, а с оригиналом. Естественно ето бьІла бьІ непозволимая роскошь утяжелять контейнерьІ до потокобезопастньІх. Здесь нужно использовать в паре с контейнером мутекс.
Записан
SLiDER
Гость
« Ответ #7 : Ноябрь 19, 2006, 21:48 »

Цитата: "Dendy"
1. Reentrant method - можно вьІзьІвать сей метод одновременно из разньІх потоков для разньІх екземпляра класса.


Хммммм. Похоже я сам неверно понимал термин Reentrant  :oops: , благодарю за доп. разяснения.
Записан
crocus
Гость
« Ответ #8 : Ноябрь 20, 2006, 19:03 »

Цитировать
to SLiDER

Нельзя ли объяснить участок кода из Вашего примера :
Код:

DataStorage():data(""){};

недогоняю....

Цитировать
Полож QStringList list в очередь - QQueue.
Пошли в главное окно сигнал.
По приходу сигнала забери из очереди и добавь в таблицу.


Сделал на основе примера SLiDERа
Код:
#ifndef LISTSTORAGE_H
#define LISTSTORAGE_H
#include <QQueue>
#include <QMutex>

class ListStorage {
QQueue<QStringList> listqueue;//это тоже не понял- сделал как в примере
QMutex mutex;
public:
ListStorage():listqueue(){};
void setData(QStringList &str){
mutex.lock();
listqueue.enqueue(str);
mutex.unlock();

}
void getData(QStringList &th_list){
mutex.lock();
while (!listqueue.isEmpty())
th_list = listqueue.dequeue();
mutex.unlock();
};
};

#endif // LISTSTORAGE_H

Код:
void NewspaperWindow::autoThread()
{
QStringList folders;
folders << "0" << "1" << "2" << "3" << "4";
for (int i = 0; i < folders.size(); ++i){
text = folders.at(i).toLocal8Bit().constData();
ThreadProcess *thread = new ThreadProcess(text, this);
thread->setListStorage(&ds);
connect(thread, SIGNAL(dataReady()), this, SLOT(ins_ShowList()));
thread->start(QThread::HighPriority);
}

}
void NewspaperWindow::ins_ShowList()
{
ds.getData(th_list);
showList(th_list);
}

Почти непонимая что делаю :|  :|

Таблица заполняется правильным количеством строк, но сами данные списка косячные - двоит и как-то бессистемно.

Похоже неправильно помещаю данные списка в очередь да и забираю наверно. Поправьте по возможности.

Попробывал на одном потоке - количество строк выдает правильно-
а вот дальше из 15 строк  записи с 1 по 14 идентичные ну и последняя как и должно быть. Похоже что кэшируется?? Последняя то строка выдается правильно.

И еще если сделать так
Код:

...............

                            list = line.split(QRegExp("\\t"));
_ds->setData(list);
emit dataReady();
                           this->msleep(1000);//!!!!!!!!!!!!!!!!!!!!!!!!!!

Таблица заполняется корректно из всех 5 потоков.
Т.е. без таймаута то ли не успевает прочитать , то ли записать???
Записан
crocus
Гость
« Ответ #9 : Ноябрь 22, 2006, 02:43 »

У SLiDERа:
Код:
SecondTh::SecondTh(QObject *parent)
    : QThread(parent){
counter = 0;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(gen_data()));
timer->start(1000);
}

Менял start в сторону уменьшения - все нормально - нет провалов, так может в моем примере нужны семафоры или waitconditions???

А может в TableWidgete не успевает отрисоваться корректно вот и заполняет чем попало???
Записан
Tonal
Гость
« Ответ #10 : Ноябрь 22, 2006, 11:09 »

Совсем чуть чуть надо изменить:
Цитата: "crocus"
<skip>
Код:
<skip>
void getData(QStringList &th_list){
mutex.lock();
if (!listqueue.isEmpty())
th_list = listqueue.dequeue();
mutex.unlock();
};
};

Код:
<skip>
void NewspaperWindow::ins_ShowList()
{
       QStringList th_list
ds.getData(th_list);
showList(th_list);
}

Записан
crocus
Гость
« Ответ #11 : Ноябрь 22, 2006, 14:02 »

Цитировать
QStringList th_list

У меня в headere определено.
Записан
SLiDER
Гость
« Ответ #12 : Ноябрь 22, 2006, 14:34 »

Цитата: "crocus"
Цитировать
to SLiDER

Нельзя ли объяснить участок кода из Вашего примера :
Код:

DataStorage():data(""){};

недогоняю....


Здесь просто происходит инициализация атрибута data через список инициализации конструктора класса. Этот список считается списком фактических параметров для вызова конструктора, производящего инициализацию.
Записан
crocus
Гость
« Ответ #13 : Ноябрь 22, 2006, 16:59 »

То ли лыжи не едут, то ли я уже того, переделал - отправляю теперь не StringList а QString
Код:
_ds.getData(th_list);
    QStringList nw_list;
nw_list = th_list.split(QRegExp("\\t"));
showList(nw_list);

И один хер без использования sleep корректны только  первая и последняя строки. З-з-з-ату-у-у-у-у-у-у-у-у-п!!!!
Записан
Tonal
Гость
« Ответ #14 : Ноябрь 23, 2006, 15:21 »

Для тех, кто в танке, объясняю:
Цитата: "Tonal"
Совсем чуть чуть надо изменить:
Цитата: "crocus"
<skip>
Код:
<skip>
void getData(QStringList &th_list){
mutex.lock();
if (!listqueue.isEmpty())
                //Здесь у тебя стояло while, т.е. ты выбирал из очереди ВСЁ        
                //но возвращал только последний элемент, остальные терял
th_list = listqueue.dequeue();
mutex.unlock();
};
};

Код:
<skip>
void NewspaperWindow::ins_ShowList()
{
//Здес нужен именно локальный объект, чтоб не замарачиваться чисткой
QStringList th_list
ds.getData(th_list);
showList(th_list);
}

Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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