Reputation: 79
I have a class with a connect_to method which I'm starting a thread in it, after calling it and joining it, I expected the thread to run in the background and the program execution would continue but it hangs in my connect_to
method until the thread execution stops. I remember I used to work with threads in C# and they ran in the background once I started them.
#ifndef _TCP_CLIENT_H_
#define _TCP_CLIENT_H_
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
void connection_thread(void *user);
class TCPClient
{
public:
SOCKET m_ConnectSocket = INVALID_SOCKET;
char m_recvbuf[BUFFER_LENGTH];
int m_recvbuflen = BUFFER_LENGTH;
struct addrinfo* m_result = NULL, *m_ptr = NULL, m_hints;
vector<PacketInfo*> m_packet_list;
PacketInfo* m_packet_data = new PacketInfo();
thread* m_conn_thread;
int state = 0;
char* ip_address;
unsigned int port = 0;
TCPClient() {}
~TCPClient() {
delete m_packet_data;
}
int Init_Sockets() {
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 0;
}
return 1;
}
int Connect_to(char* ip, unsigned int port_number) {
port = port_number;
ip_address = ip;
//thread conn_thread(connection_thread, this);
thread conn_thread([=]() {connection_thread(this); return 1; });
conn_thread.join();
state = 0;
return 1;
}
}; // end TCPClient class
void connection_thread(void *user) {
TCPClient * self = static_cast<TCPClient*>(user);
// connecting code here... //
self->state = CONNECTED;
do {
iResult = recv(self->m_ConnectSocket, self->m_recvbuf, self->m_recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
}
else if (iResult == 0) {
printf("Connection closed\n");
self->state = DISCONNECTED;
}
else {
//printf("recv failed with error: %d\n", WSAGetLastError());
}
}
while (iResult > 0);
}
#endif
The thread works as intended and is on a constant loop until the connection closes. Any idea what I'm missing?
Upvotes: 4
Views: 17568
Reputation: 264331
It is hanging because the call to join()
cause the current thread to be paused until the joining thread finishes at which point the call to join()
will return.
conn_thread.join(); // Current thread will wait
// until conn_thread finishes.
You also mention in comments that if you don't do the join then you get an abort called. This is because the destructor of thread calls terminate()
if the thread it represents is still joinble
.
Since your thread object is local it is destroyed at the end of the call to Connect_to()
.
int Connect_to(char* ip, unsigned int port_number) {
// Here you have defined a variable local to the function
// Thus its constructor is called here (which starts the thread)
thread conn_thread([=]() {connection_thread(this); return 1; });
// conn_thread.join();
}
// The destructor is called here.
// If the thread is still running at this point you will
// get a call to `terminate()`
So how do you stop that.
detach()
method.thread
belong to larger context so it is not destroyed.Calling detach()
is not a good idea. As you loose all reference to the running thread and communication with it becomes hard.
I also notice that you have a member in your class TCPClient
thread* m_conn_thread;
This does not seem to be used.
If you store your thread in this object it will last as long as the object (thus longer than the function). The object is obviously supposed to last as long as the thread since the thread access members of the object.
So I would make the following changes:
// 1 Make the member a real variable not a pointer.
std::thread m_conn_thread;
// 2 Initialize this in the constructor.
TCPClient()
: m_conn_thread() // no thread created with this constructor.
{}
// 3 Create and assing a thread to this variable.
int Connect_to(char* ip, unsigned int port_number) {
// STUFF
m_conn_thread = std::move(std::thread([=]() {connection_thread(this); return 1; }));
// MORE STUFF
}
// Add a destructor that waits for the thread to join.
~TCPClient()
{
// As the thread is using members from this object
// We can not let this obect be destroyed until
// the thread finishes executing.
m_conn_thread.join();
// At this point the thread has finished.
// Destructor can now complete.
// Note: Without the join.
// This could potentially call terminate()
// as destroying the member m_conn_thread while the
// the thread is still running is bad.
}
Upvotes: 4
Reputation: 385098
I have a class with a connect_to method which i'm starting a thread in it, after calling it and joining it, i expected the thread to run in the background and the program execution would continue but it hangs in my connect_to method until the thread execution stops
That's literally what joining a thread is supposed to do!
If you don't want to join to the thread, then simply don't. :)
Though, probably, you should at least do it in your class destructor or at some other later time, so that your main thread cannot try to end while the worker thread is still executing. Because that will end in tears...
It seems to me that this is a perfect example of why we should not write lines of code with no understanding as to why we're doing so. :) You've made an assumption about what conn_thread.join()
means, an assumption that is wrong: one of the first things to do in this situation is to read the documentation to make sure your assumptions hold. Ideally you'd have read it before writing the code, but never mind.
Upvotes: 7