Reputation: 1191
I am working on a project where I will be ingesting multiple binary files, decode them, and convert their data into a CSV. I figured the quickest way to do this would be to thread the work. Simply load the files into a queue, have the threads grab a file, work on it, convert it, output it, and then die.
What I wrote actually works great, however, I cannot figure out how to get the GUI to be responsive as I have a progress bar that I would like to update or simply have the user move the GUI to a corner while it processes the data. And I believe this is because std::thread
is just hanging up the GUI.
In my code I have the following function once a button is pressed to execute:
void MyExtractor::on_Execute_clicked()
{
QStringList binary = tlmFiles.entryList(QStringList() << "*.bin",QDir::Files);
queue.clear();
threadPool.clear();
if(binary.size() != 0)
{
foreach(QString filename, binary)
{
queue.emplace_back(inputDir + '/' + filename);
}
for (unsigned int i = 0; i < std::thread::hardware_concurrency(); ++i)
{
threadPool.emplace_back(&MyExtractor::initThread,this,std::ref(queue),std::ref(mut));
}
}
else
{
message.setText("No binary files found! Please select another folder!");
message.exec();
}
for (auto &&e : threadPool)
{
e.join();
}
}
And initThread looks like this:
void MyExtractor::initThread(std::deque<QString> &queue, std::mutex &mutex)
{
QString file;
QString toOutput = outputDir;
while(queue.size() > 0)
{
{
std::lock_guard<std::mutex> lock(mutex);
if(!queue.empty())
{
file = queue.front();
queue.pop_front();
}
}
BitExtract *bitExtractor = new BitExtract();
if(file.size() != 0)
{
bitExtractor->extract(file,toOutput);
}
delete bitExtractor;
}
}
I have been reading about QThreads. And from what I think I have been reading, it seems I need to create a separate thread to watch the work, and the other thread to watch the GUI? I am not sure if I have worded that correctly. However, I am not even sure how to go about that since I am using a std::thread
to do the conversion, and I am not sure how well QThread
will play with this. Any suggestions?
EDIT: I should make it clear that threadPool
is a std::vector<std::thread>
Upvotes: 0
Views: 1131
Reputation: 73364
As noted by @drescherjm, your problem is here:
for (auto &&e : threadPool)
{
e.join();
}
join()
won't return until the thread has completed, which means your GUI thread will be blocked inside that for-loop until all threads have exited, which is what you want to avoid. (it's always desirable for any function in the main/Qt/GUI thread to return as quickly as possible, so that Qt's GUI event loop can remain responsive)
Avoiding that is fairly straightforward -- instead of calling join()
right after the threads have been spawned, you should only call join()
on a thread after the thread has notified you that it has completed its work and is about to exit. That way join()
will never take more than a few milliseconds to return.
As for how to get a std::thread
to notify your main/GUI thread that it has finished its task, one simple way to do it is to have your std::thread
call QApplication::postEvent() just before it exits, and override the event(QEvent *) virtual method on (whatever object you passed in as the first argument to postEvent()) to handle the posted event-object (note that you can make your own subclass of QEvent
that contains whatever data you want to send to the GUI thread) by calling join()
on the std::thread
, plus whatever cleanup and result-handling code you need to execute after a thread has returned its result.
Upvotes: 3