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.
我有一个带有connect_to方法的类,我在它中启动一个线程,在调用它并加入它之后,我希望线程在后台运行并且程序执行会继续,但它会在我的connect_to方法中挂起,直到该线程执行停止。我记得我曾经在C#中使用线程,一旦我启动它们就会在后台运行。
#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?
线程按预期工作,并且在连接关闭之前处于常量循环。知道我错过了什么吗?
2 个解决方案
#1
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.
它挂起是因为对join()的调用导致当前线程暂停,直到加入的线程结束,此时对join()的调用将返回。
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
.
你还在评论中提到,如果你不进行连接,那么你会得到一个叫做的中止。这是因为如果它所代表的线程仍然可以连接,则线程的析构函数会调用terminate()。
Since your thread object is local it is destroyed at the end of the call to Connect_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.
那么你怎么阻止它。
- You can call the
detach()
method. - You can make
thread
belong to larger context so it is not destroyed.
您可以调用detach()方法。
您可以使线程属于更大的上下文,因此它不会被销毁。
Calling detach()
is not a good idea. As you loose all reference to the running thread and communication with it becomes hard.
调用detach()不是一个好主意。当你松开所有对正在运行的线程的引用并且与它的通信变得困难。
I also notice that you have a member in your class TCPClient
我还注意到你的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.
}
#2
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
我有一个带有connect_to方法的类,我在它中启动一个线程,在调用它并加入它之后,我希望线程在后台运行,程序执行会继续,但它会挂起我的connect_to方法,直到线程执行停止
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.
在我看来,这是一个完美的例子,说明为什么我们不应该编写代码行而不理解为什么我们这样做。 :)你已经假设conn_thread.join()意味着什么,这是一个错误的假设:在这种情况下要做的第一件事就是阅读文档以确保你的假设成立。理想情况下,您在编写代码之前已经阅读过它,但没关系。
#1
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.
它挂起是因为对join()的调用导致当前线程暂停,直到加入的线程结束,此时对join()的调用将返回。
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
.
你还在评论中提到,如果你不进行连接,那么你会得到一个叫做的中止。这是因为如果它所代表的线程仍然可以连接,则线程的析构函数会调用terminate()。
Since your thread object is local it is destroyed at the end of the call to Connect_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.
那么你怎么阻止它。
- You can call the
detach()
method. - You can make
thread
belong to larger context so it is not destroyed.
您可以调用detach()方法。
您可以使线程属于更大的上下文,因此它不会被销毁。
Calling detach()
is not a good idea. As you loose all reference to the running thread and communication with it becomes hard.
调用detach()不是一个好主意。当你松开所有对正在运行的线程的引用并且与它的通信变得困难。
I also notice that you have a member in your class TCPClient
我还注意到你的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.
}
#2
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
我有一个带有connect_to方法的类,我在它中启动一个线程,在调用它并加入它之后,我希望线程在后台运行,程序执行会继续,但它会挂起我的connect_to方法,直到线程执行停止
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.
在我看来,这是一个完美的例子,说明为什么我们不应该编写代码行而不理解为什么我们这样做。 :)你已经假设conn_thread.join()意味着什么,这是一个错误的假设:在这种情况下要做的第一件事就是阅读文档以确保你的假设成立。理想情况下,您在编写代码之前已经阅读过它,但没关系。