Exchange data between threads... typical question :D
-
[quote author="TheDestroyer" date="1306358791"]Would you know if I post anything here?[/quote]Yes.
-
If you're going to use threads and signals & slots (or just QObjects, for that matter), don't forget to study "this":http://developer.qt.nokia.com/wiki/ThreadsEventsQObjects wiki entry first. Also, spend time thinking how you might be able to limit the communication between threads to an absolute minimum. Threads are most effective if they can run independently. If you need lots of locking, you're not on the right track.
-
Thank you. I'll read it right now :)
-
It seems to be OK to emit an object from inside the thread out to the main thread. But how can I send data from the main thread to the thread? I tried copying a meta-object to the thread but didn't work!!
-
Please show the code that didn't work. Also, what exactly do you mean by meta-object? In Qt a meta object is a descriptive auto-generated class that says something about a QObject. Do you possibly mean meta type?
-
ah! yes I mean metatype!! :$
I'll put most of the code here, because I'm a beginner with this, and I'm going crazy not knowing how to fix this correctly! The program has worked! but I still have some problems.
The program is simply for downsampling. So it lowers the filesize within some ratio.
The problem now is, if the file size is huge, the program freezes!
and the other problem is, I want to signal the object "data" by reference! is this possible?here's the most of the code:
Thread code:
@
void downSamplingThread::run()
{
passedData data;
data = dataPassed;
downSample(data.inputFile,data.outputFile,data.dataUnitSize,data.downSamplingRate, data);}
qint16 downSamplingThread::downSample(QString inputFile, QString outputFile, quint64 dataUnitSize, quint64 downSamplingRate, passedData &data)
{
double progressValue = 0;
int progressLimit = 0;
qint64 fileSize;
std::fstream fileReader(inputFile.toStdString().c_str(),std::ios::in | std::ios::binary);
QFile file(inputFile.toStdString().c_str());
if(!fileReader.is_open())
{
return 1;
}
if(inputFile == outputFile && inputFile.length() != 0)
{
return 3;
}
std::fstream fileWriter(outputFile.toStdString().c_str(),std::ios::out | std::ios::binary);
if(!fileWriter.good())
{
return 2;
}
quint64 dataUnit = dataUnitSize;
quint64 downsamplingRate = downSamplingRate;
char* buffer = new char[dataUnitdownSamplingRate];
fileSize = file.size();
file.close();
while(!fileReader.eof())
{
fileReader.read(buffer,dataUnitdownsamplingRate);
if(!fileReader.eof())
fileWriter.write(buffer,dataUnit);
progressValue = (double)(100*fileReader.tellg())/fileSize;
if(progressLimit < floor(progressValue))
{
progressLimit++;
data.progress = progressLimit;
emit progressTimeout(data);
}
}
data.enableButton = 1;
data.progress = 100;
emit progressTimeout(data);
return 0;
}
@
in dialog for sending the command:@
...
connect(startButton,SIGNAL(clicked()),this,SLOT(downSampleCommand()));
connect(thread,SIGNAL(progressTimeout(passedData)),this,SLOT(updateProgress(passedData)));
...
void Dialog::downSampleCommand()
{
statusLabel->setText("Process started...");
startButton->setEnabled(0);
data.dataUnitSize = dataUnitSizeSpinbox->value();
data.inputFile = inputPathLineEdit->text();
data.outputFile = outputPathLineEdit->text();
data.downSamplingRate = downSamplingRateSpinbox->value();
data.enableButton = 0;
thread->dataPassed = this->data; //copy
thread->start();
qint16 outputReturn = 0;
//qint16 outputReturn = thread->downSample(inputPathLineEdit->text(),outputPathLineEdit->text(),dataUnitSizeSpinbox->value(),downSamplingRateSpinbox->value(),data);
if(outputReturn == 1)
{
statusLabel->setText("Error reading file!");
}
else if(outputReturn == 2)
{
statusLabel->setText("Error writing file!");
}
else if(outputReturn == 3)
{
statusLabel->setText("Input and output files are the same. Error!");
}
else if(outputReturn == 0)
{
statusLabel->setText("Done!");
}
}
void Dialog::setLastFilePath(QString path)
{
*lastFile = path;
inputFileDialog->setDirectory(path);
outputFileDialog->setDirectory(path);
}
void Dialog::updateProgress(passedData data)
{
progressBar->setValue(data.progress);
startButton->setEnabled(data.enableButton);
//QMessageBox *msg = new QMessageBox;
//msg->setText(QVariant(data.inputFile).toString());
//msg->show();
}
@metatype:
@
#include <QColor>
#include <QDebug>
#include <QMetaType>
#include <QRect>class passedData
{
public:
passedData();
~passedData();
passedData(const passedData &src);
passedData& operator=(const passedData &src);public:
QString inputFile;
QString outputFile;
quint64 dataUnitSize;
quint64 downSamplingRate;
bool enableButton;
int progress;
signals:
};Q_DECLARE_METATYPE(passedData);
#endif // PASSEDDATA_H
#include "passeddata.h"
passedData::passedData()
{
}
passedData::~passedData()
{}
passedData::passedData(const passedData &src)
{
this->dataUnitSize = src.dataUnitSize;
this->downSamplingRate = src.downSamplingRate;
this->enableButton = src.enableButton;
this->inputFile = src.inputFile;
this->outputFile = src.outputFile;
this->progress = src.progress;
}
passedData& passedData::operator =(const passedData &src)
{
this->dataUnitSize = src.dataUnitSize;
this->downSamplingRate = src.downSamplingRate;
this->enableButton = src.enableButton;
this->inputFile = src.inputFile;
this->outputFile = src.outputFile;
this->progress = src.progress;
return *this;
}
@and in main, I called
@
qRegisterMetaType<passedData>();
@The program's idea is very simple. It just reads a chunk of the file, and writes a part of the chunk again. It didn't work on a file with size 30 GB (which is a typical size we need).
Please tell me how to make this program as effective as possible. I know I'm being dependent somehow, but I have been reading about this for long time, and I'm not getting the idea!!! :(
Thank you for any efforts :-)
-
Hm, from reading this, I wonder why exactly you want to pass the data between threads? If the program will only be down sampling, your worker should be doing the down sampling and report some progress. The setup I'd then use is practically the same, but I would only be releasing the progress counter into the world, maybe a status string if it was really important.
-
Indeed, I agree with Franzk. Quoting myself:
[quote]Also, spend time thinking how you might be able to limit the communication between threads to an absolute minimum. Threads are most effective if they can run independently. If you need lots of locking, you’re not on the right track.[/quote]In this case, indeed, this would probably be only sending some progress information (and not too frequently either!)
-
Thanks for the answers guys :)... another problem is present after saving the first one :D
This program is just a sample. I'm learning, and practicing!! and actually I find it the same if I send progress or the whole data object for a very simple reason! I'm sending a reference (in the new implementation), not an object!!! I just learned how to do it!! so it's just a pointer! (and it was my purpose from the very beginning). Is it possible or legal to emit a simple variable like long int without it being a metatype rather than emitting a whole object?
The only reason I didn't send the progress value alone (which I do only 100 times during the process) is because I don't want to create another metatype class that contains the progress... so adding the progress to "data" was the fastest way. Don't you agree guys?
Actually, guys, I'm facing another problem! I'm unable to control the thread in means of starting it and closing it. For example, if the user inputs a wrong path, or does anything wrong, I want the thread to close and another one to start from the next "start" button clicked. I tried resolving this many ways. But the only way was creating a new thread on every "Start" button clicked, which implies ignoring the previous thread instantiations... because waiting, terminating, exiting or quitting it isn't working at all!!! so on every "Start" button clicked, a call "new downSamplingThread" is done, and new connections are done. But this wastes memory! how can I avoid this? how can I make sure the threads are to be deleted?
@
void Dialog::downSampleCommand()
{
thread = new downSamplingThread(this);
connect(thread,SIGNAL(progressTimeout(passedData)),this,SLOT(updateProgress(passedData)));
connect(thread,SIGNAL(sendOutputReturn(qint16)),this,SLOT(getReturnValue(qint16)));statusLabel->setText("Process started..."); startButton->setEnabled(0); data.dataUnitSize = dataUnitSizeSpinbox->value(); data.inputFile = inputPathLineEdit->text(); data.outputFile = outputPathLineEdit->text(); data.downSamplingRate = downSamplingRateSpinbox->value(); data.enableButton = 0; thread->dataPassed = this->data; //copy thread->start();
}
void Dialog::updateProgress(const passedData &data)
{
progressBar->setValue(data.progress);
startButton->setEnabled(data.enableButton);
//QMessageBox *msg = new QMessageBox;
//msg->setText(QVariant(data.inputFile).toString());
//msg->show();
}
void Dialog::getReturnValue(qint16 outputReturn)
{
if(outputReturn == 1)
{
statusLabel->setText("Error reading file!");
}
else if(outputReturn == 2)
{
statusLabel->setText("Error writing file!");
}
else if(outputReturn == 3)
{
statusLabel->setText("Input and output files are the same. Error!");
}
else if(outputReturn == 0)
{
statusLabel->setText("Done!");
}
thread->terminate();
thread->quit();
thread->exit(1);
//thread->wait();
//delete thread;
startButton->setEnabled(1);
}
void downSamplingThread::run()
{
passedData data;
data = dataPassed;
downSample(data.inputFile,data.outputFile,data.dataUnitSize,data.downSamplingRate, data);}
qint16 downSamplingThread::downSample(QString inputFile, QString outputFile, quint64 dataUnitSize, quint64 downSamplingRate, passedData &data)
{
double progressValue = 0;
int progressLimit = 0;
quint64 fileSize;
QFile fileReader(inputFile);
fileReader.open(QFile::ReadOnly);
if(fileReader.error())
{
emit sendOutputReturn(1);
//this->quit();
//this->terminate();
//this->exit();
return 1;
}
QFile fileWriter(outputFile);
fileWriter.open(QFile::WriteOnly);
if(inputFile == outputFile && inputFile.length() != 0)
{
emit sendOutputReturn(3);
//this->quit();
//this->terminate();
//this->exit();
return 3;
}
if(fileWriter.error())
{
emit sendOutputReturn(2);
//this->quit();
//this->terminate();
//this->exit();
return 2;
}
quint64 dataUnit = dataUnitSize;
quint64 downsamplingRate = downSamplingRate;
char* buffer = new char[dataUnitdownSamplingRate];
fileSize = fileReader.size();
quint64 i = 0;
while(i < fileSize)
{
fileReader.read(buffer,dataUnitdownsamplingRate);
fileWriter.write(buffer,dataUnit);
progressValue = (double)(100*fileReader.pos())/fileSize;
if(progressLimit < floor(progressValue))
{
progressLimit++;
data.progress = progressLimit;
emit progressTimeout(data);
}
i += dataUnit * downsamplingRate;
}
data.enableButton = 1;
data.progress = 100;
emit progressTimeout(data);
sendOutputReturn(0);
return 0;
}@
so how can I stop this thread after failing to open the file or in general emit an error in a way that saves memory and doesn't block the main window. Cuz if I do anything like delete thread; or thread->wait()... the window freezes for some long time!!!!!
-
Btw, I used QFile in the new implementation because fstream was, for some reason, unable to read long files. Apparently "long int"'s precission is not enough to catch 30 gb... is this problem common? any idea? I'm using in the new implementation quint64, which works! Anything you think I should know about this?
-
come on guys!!! It's the last part of the problem :(
-
Please allow people some time, especially in the weekend, to chew through the code you dumped on us.
-
Sure, buddy :-)