When the main exits where does the console output go? - multithreading

#include<iostream>
#include<thread>
using namespace std;
void func()
{
for (int i = 0; i < 10000; i++)cout << "Print" << endl;
}
int main()
{
thread t(func);
t.detach();
cout << "Exit" << endl;
return 0;
}
In the above code, when the main exits, where does the "Print"text would be gone,since it doesn't have an output stream? Is there any dummy stream for inserting data which has no use?

When main exits it calls exit which terminates all threads, regardless detached or not. This is because exit terminates the entire process.
The C++ runtime runs main as exit(main(argc, argv)), so that returning from main causes exit to be called.
You can terminate your main thread, if you wish, by calling pthread_exit. In this case the main thread will not return from main and will not call exit. The application will keep running until some other thread calls exit or all threads terminate (or the application crashes). This is how it works on Linux, not sure about Windows.
std::cout object and the other standard streams are available at least until exit is called. These streams are initialized using Schwarz Counter idiom, which makes sure they get initialized before its first use and destroyed after the last user is gone. In other words, if you have a global object with a constructor and destructor, which gets initialized before main is entered and destroyed after (when exit is called), that standard stream is still going to be available in that global object destructor. Basically, there is a reference counter associated with each standard stream, each translation unit (object file) increments this reference counter on the startup and decrements on termination.
ISO/IEC 14882:2011(E) says:
27.4 Standard iostream objects
27.4.1.2 The objects [the standard streams] are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution†. The objects are not destroyed during program execution. The results of including in a translation unit shall be as if defined an instance of ios_base::Init with static storage duration. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.
† Constructors and destructors for static objects can access these objects to read input from stdin or write output to stdout or stderr.

Related

Properties of pthread_exit function : which one is right?

In the CSAPP book Section 12.3, They said..
The thread terminates explicitly by calling the pthread_exit function. If the main thread calls pthread_exit, it waits for all other peer threads to terminate and then terminates main thread and the entire process with a return value of thread_return.
However in the man page of pthread_exit : https://man7.org/linux/man-pages/man3/pthread_exit.3.html
Performing a return from the start function of any thread other than the main thread results in an implicit call to pthread_exit(), using the function's return value as the thread's exit status.
To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3).
Two descriptions about pthread_exit are different. First one said main thread will wait for peer but not on second.
Therefore I write a code to ensure correct property.
(I borrow some code lines from When the main thread exits, do other threads also exit?)
(Thanks to https://stackoverflow.com/users/959183/laifjei)
Since pthread_cancel is called before pthread_exit, main thread cancel t1 thread successfully and the result is like,,
However, when I modify a code as '42 line -> add //' and '44 line -> delete //', main thread cannot cancel t1 since it was already terminated. Therefore the following result is looks like,,
Finally, I conclude that man page's property is correct. Am I right?
Why does CSAPP book said that "it waits for all other peer threads to terminate"?
Two descriptions about pthread_exit are different. First one said main thread will wait for peer but not on second.
Not very different, and not in a way that you can easily distinguish by most means.
In particular, regardless of whether the main thread terminates immediately or waits for other threads to terminate before doing so, the pthread_exit() function is like the exit() function in that it does not return. Observing that statements inserted into your test program between the pthread_exit() call and the end of main are not executed does yield any information that helps you determine the relative sequence of thread terminations.
For that reason, the question is also largely moot. Although there indeed are ways in which the difference can be observed, it is rarely significant.
Nevertheless, here's a better example:
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
pthread_t main_thread;
void *wait_for_main(void *unused) {
void *main_rval;
// Wait for the main thread to terminate
if ((errno = pthread_join(main_thread, &main_rval)) != 0) {
perror("pthread_join");
} else {
fputs("The main thread was successfully joined\n", stderr);
}
fflush(stderr);
return NULL;
}
int main(void) {
pthread_t child_thread;
main_thread = pthread_self();
if ((errno = pthread_create(&child_thread, NULL, wait_for_main, NULL)) != 0) {
perror("pthread_create");
} else {
fputs("The child thread was successfully started\n", stderr);
}
pthread_exit(NULL);
}
That program runs successfully, printing ...
The child thread was successfully started
The main thread was successfully joined
This shows that the main thread indeed terminated (because it was successfully joined), and that the other thread continued to run afterward (because it wrote its message to stderr).
You go on to ask ...
Why does CSAPP book said that "it waits for all other peer threads to terminate"?
... but no one other than Bryant, O'Hallaron, or one of their editors could definitively answer the question (and maybe not all -- or any -- of those). Here are some possibilities:
The book is just wrong. It happens.
The book is unclear or imprecise, in that it means the "it" that waits to be the overall program, the operating system, or some other variation on "something other than the main thread".
Or my actual best guess:
The book is is describing behavior from an operating system perspective, whereas the Pthreads documentation is describing it from a C-language perspective. It may well be that the OS thread that is the process's main one indeed is the thing that waits for others to terminate, but its C-language semantics within the running program terminate with the pthread_exit(). That is the book is talking about pthread implementation details, not documented, observable pthread semantics.

QT Multithreading Data Pass from Main Thread to Worker Thread

I am using multithreading in my QT program. I need to pass data to the worker object that lives in the worker thread from the main gui thread. I created a setData function in a QObject subclass to pass all the necessary data from the main gui thread. However I verified the function is called from the main thread by looking at QThread::currentThreadId() in the setData function. Even though the worker object function is called from the main thread does this ensure that the worker thread still has its own copy of the data as is required for a reentrant class? Keep in mind this is happening before the worker thread is started.
Also if basic data types are used in a class without dynamic memory and no static global variables is that class reentrant as long as all of its other member data is reentrant? (it's got reentrant data members like qstrings, qlists etc in addition the the basic ints bools etc)
Thanks for the help
Edited new content:
My main question was simply is it appropriate to call a QObject subclass method living in another thread from the main gui thread in order to pass my data to the worker thread to be worked on (in my case custom classes containing backup job information for long-pending file scans and copies for data backup). The data pass all happens before the thread is started so there's no danger of both threads modifying the data at once (I think but I'm no multithreading expert...) It sounds like the way to do this from your post is to use a signal from the main thread to a slot in the worker thread to pass the data. I have confirmed my data backup jobs are reentrant so all I need to do is assure that the worker thread works on its own instances of these classes. Also the transfer of data currently done by calling the QObject subclass method is done before the worker thread starts - does this prevent race conditions and is it safe?
Also here under the section "Accessing QObject Subclasses from Other Threads" it looks a little dangerous to use slots in the QObject subclass...
OK here's the code I've been busy recently...
Edited With Code:
void Replicator::advancedAllBackup()
{
updateStatus("<font color = \"green\">Starting All Advanced Backups</font>");
startBackup();
worker = new Worker;
worker->moveToThread(workerThread);
setupWorker(normal);
QList<BackupJob> jobList;
for (int backupCount = 0; backupCount < advancedJobs.size(); backupCount++)
jobList << advancedJobs[backupCount];
worker->setData(jobList);
workerThread->start();
}
The startBackup function sets some booleans and updates the gui.
the setupWorker function connects all signals and slots for the worker thread and worker object.
the setData function sets the worker job list data to that of the backend and is called before the thread starts so there is no concurrency.
Then we start the thread and it does its work.
And here's the worker code:
void setData(QList<BackupJob> jobs) { this->jobs = jobs; }
So my question is: is this safe?
There are some misconceptions in your question.
Reentrancy and multithreading are orthogonal concepts. Single-threaded code can be easily forced to cope with reentrancy - and is as soon as you reenter the event loop (thus you shouldn't).
The question you are asking, with correction, is thus: Are the class's methods thread-safe if the data members support multithreaded access? The answer is yes. But it's a mostly useless answer, because you're mistaken that the data types you use support such access. They most likely don't!
In fact, you're very unlikely to use multithread-safe data types unless you explicitly seek them out. POD types aren't, most of the C++ standard types aren't, most Qt types aren't either. Just so that there are no misunderstandings: a QString is not multithread-safe data type! The following code is has undefined behavior (it'll crash, burn and send an email to your spouse that appears to be from an illicit lover):
QString str{"Foo"};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run([&]{ str.append("bar"); });
The follow up questions could be:
Are my data members supporting multithreaded access? I thought they did.
No, they aren't unless you show code that proves otherwise.
Do I even need to support multithreaded access?
Maybe. But it's much easier to avoid the need for it entirely.
The likely source of your confusion in relation to Qt types is their implicit sharing semantics. Thankfully, their relation to multithreading is rather simple to express:
Any instance of a Qt implicitly shared class can be accessed from any one thread at a given time. Corollary: you need one instance per thread. Copy your object, and use each copy in its own thread - that's perfectly safe. These instances may share data initially, and Qt will make sure that any copy-on-writes are done thread-safely for you.
Sidebar: If you use iterators or internal pointers to data on non-const instances, you must forcibly detach() the object before constructing the iterators/pointers. The problem with iterators is that they become invalidated when an object's data is detached, and detaching can happen in any thread where the instance is non-const - so at least one thread will end up with invalid iterators. I won't talk any more of this, the takeaway is that implicitly shared data types are tricky to implement and use safely. With C++11, there's no need for implicit sharing anymore: they were a workaround for the lack of move semantics in C++98.
What does it mean, then? It means this:
// Unsafe: str1 potentially accessed from two threads at once
QString str1{"foo"};
QtConcurrent::run([&]{ str1.apppend("bar"); });
str1.append("baz");
// Safe: each instance is accessed from one thread only
QString str1{"foo"};
QString str2{str1};
QtConcurrent::run([&]{ str1.apppend("bar"); });
str2.append("baz");
The original code can be fixed thus:
QString str{"Foo"};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run([=]() mutable { str.append("bar"); });
This isn't to say that this code is very useful: the modified data is lost when the functor is destructed within the worker thread. But it serves to illustrate how to deal with Qt value types and multithreading. Here's why it works: copies of str are taken when each instance of the functor is constructed. This functor is then passed to a worker thread to execute, where its copy of the string is appended to. The copy initially shares data with the str instance in the originating thread, but QString will thread-safely duplicate the data. You could write out the functor explicitly to make it clear what happens:
QString str{"Foo"};
struct Functor {
QString str;
Functor(const QString & str) : str{str} {}
void operator()() {
str.append("bar");
}
};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run(Functor(str));
How do we deal with passing data using Qt types in and out of a worker object? All communication with the object, when it is in the worker thread, must be done via signals/slots. Qt will automatically copy the data for us in a thread-safe manner so that each instance of a value is ever only accessed in one thread only. E.g.:
class ImageSource : public QObject {
QImage render() {
QImage image{...};
QPainter p{image};
...
return image;
}
public:
Q_SIGNAL newImage(const QImage & image);
void makeImage() {
QtConcurrent::run([this]{
emit newImage(render());
});
}
};
int main(int argc, char ** argv) {
QApplication app...;
ImageSource source;
QLabel label;
label.show();
connect(source, &ImageSource::newImage, &label, [&](const QImage & img){
label.setPixmap(QPixmap::fromImage(img));
});
source.makeImage();
return app.exec();
}
The connection between the source's signal and the label's thread context is automatic. The signal happens to be emitted in a worker thread in the default thread pool. At the time of signal emission, the source and target threads are compared, and if different, the functor will be wrapped in an event, the event posted the label, and the label's QObject::event will run the functor that sets the pixmap. This is all thread-safe and leverages Qt to make it almost effortless. The target thread context &label is critically important: without it, the functor would run in the worker thread, not the UI thread.
Note that we didn't even have to move the object to a worker thread: in fact, moving a QObject to a worker thread should be avoided unless the object does need to react to events and does more than merely generate a piece of data. You'd typically want to move e.g. objects that deal with communications, or complex application controllers that are abstracted from their UI. Mere generation of data can be usually done using QtConcurrent::run using a signal to abstract away the thread-safety magic of extracting the data from the worker thread to another thread.
In order to use Qt's mechanisms for passing data between threads with queues, you cannot call the object's function directly. You need to either use the signal/slot mechanism, or you can use the QMetaObject::invokeMethod call:
QMetaObject::invokeMethod(myObject, "mySlotFunction",
Qt::QueuedConnection,
Q_ARG(int, 42));
This will only work if both the sending and receiving objects have event queues running - i.e. a main or QThread based thread.
For the other part of your question, see the Qt docs section on reentrancy:
http://doc.qt.io/qt-4.8/threads-reentrancy.html#reentrant
Many Qt classes are reentrant, but they are not made thread-safe,
because making them thread-safe would incur the extra overhead of
repeatedly locking and unlocking a QMutex. For example, QString is
reentrant but not thread-safe. You can safely access different
instances of QString from multiple threads simultaneously, but you
can't safely access the same instance of QString from multiple threads
simultaneously (unless you protect the accesses yourself with a
QMutex).

mutex destroyed while busy

There is a singleton object of EventHandler class to receive events from the mainthread. It registers the input to a vector and creates a thread that runs a lambda function that waits for some time before deleting the input from the vector to prevent repeated execution of the event for this input for some time.
But I'm getting mutex destroyed while busy error. I'm not sure where it happened and how it happened. I am not even sure what it meant either because it shouldn't be de-constructed ever as a singleton object. Some help would be appreciated.
class EventHandler{
public:
std::mutex simpleLock;
std::vector<UInt32> stuff;
void RegisterBlock(UInt32 input){
stuff.push_back(input);
std::thread removalCallBack([&](UInt32 input){
std::this_thread::sleep_for(std::chrono::milliseconds(200));
simpleLock.lock();
auto it = Find(stuff, input);
if (it != stuff.end())
stuff.erase(it);
simpleLock.simpleLock.unlock();
}, input)
removalCallBack.detach();
}
virtual EventResult ReceiveEvent(UInt32 input){
simpleLock.lock();
if (Find(stuff, input) != stuff.end()){
RegisterBlock(input));
//dostuff
}
simpleLock.simpleLock.unlock();
}
};
What is happening is that a thread is created
std::thread removalCallBack([&](UInt32 input){
std::this_thread::sleep_for(std::chrono::milliseconds(200));
simpleLock.lock();
...
removalCallBack.detach();
And then since removalCallBack is a local variable to the function RegisterBlock, when the function exits, the destructor for removalCallBack gets called which invokes std::terminate()
Documentation for thread destructor
~thread(); (since C++11)
Destroys the thread object. If *this still has an associated running thread (i.e. joinable() == true), std::terminate() is called.
but depending on timing, simpleLock is still owned by the thread (is busy) when the thread exits which according to the spec leads to undefined behavior, in your case the destroyed while busy error.
To avoid this error, you should either allow the thread to exist after the function exits (e.g. not make it a local variable) or block until the thread exits before the function exits using thread::join
Dealing with cleaning up after threads can be tricky especially if they are essentially used as different programs occupying the same address space, and in those cases many times a manager thread just like you thought of is created whose only job is to reclaim thread related resources. Your situation is a little easier because of the simplicity of the work done in the thread created by removalCallBack, but there still is cleanup to do.
If the thread object is going to be created by new, then although system resources used by the system thread the C++ thread object represents will get cleaned up, but the memory the object uses will remain allocated until delete is called.
Also, consider if the program exits while there are threads running, then the threads will be terminated, but if there is a mutex locked when that happens, once again there will be undefined behavior.
What is usually done to guarantee that a thread is no longer running is to join with it, but though this doesn't say, the pthread_join man page states
Once a thread has been detached, it can't be joined with pthread_join(3) or be made joinable again.

Long-running / blocking operations in boost asio handlers

Current Situation
I implemented a TCP server using boost.asio which currently uses a single io_service object on which I call the run method from a single thread.
So far the server was able to answer the requests of the clients immediately, since it had all necessary information in the memory (no long-running operations in the receive handler were necessary).
Problem
Now requirements have changed and I need to get some information out of a database (with ODBC) - which is basically a long-running blocking operation - in order to create the response for the clients.
I see several approaches, but I don't know which one is best (and there are probably even more approaches):
First Approach
I could keep the long running operations in the handlers, and simply call io_service.run() from multiple threads. I guess I would use as many threads as I have CPU cores available?
While this approach would be easy to implement, I don't think I would get the best performance with this approach because of the limited number of threads (which are idling most of the time since database access is more an I/O-bound operation than a compute-bound operation).
Second Approach
In section 6 of this document it says:
Use threads for long running tasks
A variant of the single-threaded design, this design still uses a single io_service::run() thread for implementing protocol logic. Long running or blocking tasks are passed to a background thread and, once completed, the result is posted back to the io_service::run() thread.
This sounds promising, but I don't know how to implement that. Can anyone provide some code snippet / example for this approach?
Third Approach
Boris Schäling explains in section 7.5 of his boost introduction how to extend boost.asio with custom services.
This looks like a lot of work. Does this approach have any benefits compared to the other approaches?
The approaches are not explicitly mutually exclusive. I often see a combination of the first and second:
One or more thread are processing network I/O in one io_service.
Long running or blocking tasks are posted into a different io_service. This io_service functions as a thread pool that will not interfere with threads handling network I/O. Alternatively, one could spawn a detached thread every time a long running or blocking task is needed; however, the overhead of thread creation/destruction may a noticeable impact.
This answer that provides a thread pool implementation. Additionally, here is a basic example that tries to emphasize the interaction between two io_services.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
/// #brief Background service will function as a thread-pool where
/// long-standing blocking operations may occur without affecting
/// the network event loop.
boost::asio::io_service background_service;
/// #brief The main io_service will handle network operations.
boost::asio::io_service io_service;
boost::optional<boost::asio::io_service::work> work;
/// #brief ODBC blocking operation.
///
/// #brief data Data to use for query.
/// #brief handler Handler to invoke upon completion of operation.
template <typename Handler>
void query_odbc(unsigned int data,
Handler handler)
{
std::cout << "in background service, start querying odbc\n";
std::cout.flush();
// Mimic busy work.
boost::this_thread::sleep_for(boost::chrono::seconds(5));
std::cout << "in background service, posting odbc result to main service\n";
std::cout.flush();
io_service.post(boost::bind(handler, data * 2));
}
/// #brief Functions as a continuation for handle_read, that will be
/// invoked with results from ODBC.
void handle_read_odbc(unsigned int result)
{
std::stringstream stream;
stream << "in main service, got " << result << " from odbc.\n";
std::cout << stream.str();
std::cout.flush();
// Allow io_service to stop in this example.
work = boost::none;
}
/// #brief Mocked up read handler that will post work into a background
/// service.
void handle_read(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "in main service, need to query odbc" << std::endl;
typedef void (*handler_type)(unsigned int);
background_service.post(boost::bind(&query_odbc<handler_type>,
21, // data
&handle_read_odbc) // handler
);
// Keep io_service event loop running in this example.
work = boost::in_place(boost::ref(io_service));
}
/// #brief Loop to show concurrency.
void print_loop(unsigned int iteration)
{
if (!iteration) return;
std::cout << " in main service, doing work.\n";
std::cout.flush();
boost::this_thread::sleep_for(boost::chrono::seconds(1));
io_service.post(boost::bind(&print_loop, --iteration));
}
int main()
{
boost::optional<boost::asio::io_service::work> background_work(
boost::in_place(boost::ref(background_service)));
// Dedicate 3 threads to performing long-standing blocking operations.
boost::thread_group background_threads;
for (std::size_t i = 0; i < 3; ++i)
background_threads.create_thread(
boost::bind(&boost::asio::io_service::run, &background_service));
// Post a mocked up 'handle read' handler into the main io_service.
io_service.post(boost::bind(&handle_read,
make_error_code(boost::system::errc::success), 0));
// Post a mockup loop into the io_service to show concurrency.
io_service.post(boost::bind(&print_loop, 5));
// Run the main io_service.
io_service.run();
// Cleanup background.
background_work = boost::none;
background_threads.join_all();
}
And the output:
in main service, need to query odbc
in main service, doing work.
in background service, start querying odbc
in main service, doing work.
in main service, doing work.
in main service, doing work.
in main service, doing work.
in background service, posting odbc result to main service
in main service, got 42 from odbc.
Note that the single thread processing the main io_service posts work into the background_service, and then continues to process its event loop while the background_service blocks. Once the background_service gets a result, it posts a handler into the main io_service.
We have same long-running tasks in our server (a legacy protocol with storages). So our server is running 200 threads to avoid blocking service (yes, 200 threads is running io_service::run). Its not too great thing, but works well for now.
The only problem we had is asio::strand which uses so-called "implementations" which gets locked when hadler is currently called. Solved this via increase this strands butckets and "deattaching" task via io_service::post without strand wrap.
Some tasks may run seconds or even minutes and this does work without issues at the moment.

QtConcurrent threading is slow!! What am I doing wrong?

Why is my qtconcurrent::run() call just as slow as calling the member function through the object??
(Ex: QtConcurrent::run(&db, &DBConnect::loadPhoneNumbers) is just as slow as calling db.loadPhoneNumbers())
Read below for futher explanation
I've been trying to create a thread via QtConcurrent::run to help speed up data being sent to a SQL database table. I am taking a member variable which is a QMap and iterating through it to send each key+value to the database.
Member function for the QtConcurrent::run() call:
void DBConnect::loadPhoneNumbers()
{
//m_phoneNumbers is a private QMap member variable in DBConnect
qDebug() << "\t[!] Items to send: " << m_phoneNumbers.size();
QSqlQuery query;
qDebug() << "\t[!] Using loadphonenumbers thread: " << QThread::currentThread();
qDebug() << "\t[!] Ideal Num of Threads: " << QThread::idealThreadCount();
bool isLoaded = false;
QMap<QString,QString>::const_iterator tmp = m_phoneNumbers.constBegin();
while(tmp != m_phoneNumbers.constEnd())
{
isLoaded = query.exec(QString("INSERT INTO "+m_mtable+" VALUES('%1','%2')").arg(tmp.key()).arg(tmp.value()));
if(isLoaded == false)
{
qDebug() << "\r\r[X] ERROR: Could\'t load number " << tmp.key() << " into table " << m_mtable;
qDebug() << query.lastError().text();
}
tmp++;
}
}
main.cpp section that calls the thread
DBConnect db("QODBC", myINI.getSQLServer(),C_DBASE,myINI.getMTable(), myINI.getBTable());
db.startConnect();
//...more code here
qDebug() << "\n[*] Using main thread: " << QThread::currentThread() << endl;
//....two qtconcurrent::run() threads started and finished here (not shown)
qDebug() << "\n[*] Sending numbers to Database...";
QFuture<void> dbFuture = QtConcurrent::run(&db, &DBConnect::loadPhoneNumbers);
dbFuture.waitForFinished();
My understanding of the situation
From my understanding, this thread will run under a new pool of threads seperate from the main thread. What I am seeing is not the case (note there are 2 other QtConcurrent::run() calls before this one for the database, all left to finish before continuing to database call)
Now I thought about using QtConcurrent::map() / mapped() but couldn't get it to work properly with a QMap. (Couldn't find any examples to help out with either but that is besides the matter... was just an FYI in case someone asks why I didn't use one)
Have been doing some "debug" work to find out whats happening and in my tests I use QThread::currentThread() to find which thread I am currently making a call from. This is what is happening for the various threads in my program. (All qtconcurrent::run() calls are made in main.cpp FYI... not sure if that makes a difference)
Check what is main thread: on QThread(0x5d2cd0)
Run thread 1: on QThread(0x5dd238, name = "Thread (pooled)")
Run thread 2: on QThread(0x5d2cd0)
Run thread 3 (loadPhoneNumbers function): on QThread(0x5d2cd0)
As seen above, other than the first qtconcurrent::run() call, everything else is on the main thread (o.O)
Questions:
From my understanding, all my threads (all qtconcurrent::run) should be on their own thread (only first one is). Is that true or am I missing something?
Second, is my loadPhoneNumebrs() member function thread safe?? (Since I am not altering anything from what I can see)
Biggest question:
Why is my loadPhoneNumbers() qtconcurrent::run call just as slow as if I just called the member function? (ex: db.loadPhoneNumbers() is just as slow as the qtconcurrent::run() version)
Any help is much appreciated!
Threads don't magically speed things up, they just make it so you can continue doing other stuff while it's happening in the background. When you call waitForFinished(), your main thread won't continue until the load phone numbers thread is finished, essentially negating that advantage. Depending on the implementation, that may be why your currentThread() is showing the same as main, because the wait is already happening.
Probably more significant in terms of speed would be to build a single query that inserts all the values in the list, rather than a separate query for each value.
According to QtSql documentation:
A connection can only be used from within the thread that created it.
Moving connections between threads or creating queries from a
different thread is not supported.
It works anyway because ODBC itself supports multithreaded access to a single ODBC handle. But since you are only using one connection, all queries are probably serialized by ODBC as if there was only a single thread (see for example what Oracle's ODBC driver does).
waitForFinished() calls a private function stealRunnable() that, as its name implies, takes a not yet started task from the QFuture queue an runs it in the current thread.

Resources