【问题标题】:Running thread in background in c++11在c ++ 11中在后台运行线程
【发布时间】:2015-05-15 17:58:27
【问题描述】:

我有一个带有 connect_to 方法的类,我在其中启动一个线程,在调用它并加入它之后,我希望线程在后台运行并且程序执行将继续,但它挂在我的 @987654321 @方法直到线程执行停止。我记得我曾经在 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

线程按预期工作,并且一直处于循环状态,直到连接关闭。知道我缺少什么吗?

【问题讨论】:

    标签: c++ multithreading


    【解决方案1】:

    我有一个带有 connect_to 方法的类,我在其中启动一个线程,调用它并加入它后,我希望线程在背景和程序执行将继续,但 它挂在我的 connect_to 方法中,直到线程执行停止

    这就是加入线程应该做的事情!

    如果您不想加入该线程,那么就不要加入。 :)

    尽管如此,您可能至少应该在您的类析构函数中或稍后的某个时间执行此操作,这样您的主线程就无法在工作线程仍在执行时尝试结束。因为那会以眼泪收场……

    在我看来,这是一个完美的例子,说明为什么我们不应该在不了解为什么我们这样做的情况下编写代码行。 :) 您对conn_thread.join() 的含义做出了假设,这是错误的假设:在这种情况下首先要做的事情之一是阅读文档以确保您的假设成立。理想情况下,您应该在编写代码之前阅读过它,但没关系。

    【讨论】:

    • 很遗憾,如果我不加入它,我会收到一个错误框:调试错误! R6010 - abort() 已被调用。
    • @Skrakle:这不是加入它的理由(其记录的目的实际上与您想做的相反)。这是找出为什么调用abort() 的原因。无论如何,原因是可能我在第三段中所说的......
    • @LightnessRacesinOrbit 有时,反复试验会导致理解原因。根据个人的学习风格,他们可能必须通过编写代码来探索代码以了解其行为方式,从而了解他们是否需要以及为什么需要(或不需要)。如果最终结果都相同,这不一定是坏事:用户了解他们需要做什么以及为什么。
    • @Javia1492:一切都很好,但仍然没有借口不阅读文档,无论是在编程之前还是在对您的程序无法按预期工作感到困惑时。这不是“试错”;这是“试用并寻求帮助”。
    • @LightnessRacesinOrbit:是的,我同意。盲目地进入它说“哦,它不起作用”是不正确的。
    【解决方案2】:

    它挂起是因为对join() 的调用导致当前线程暂停,直到加入线程完成,此时对join() 的调用将返回。

     conn_thread.join();   // Current thread will wait
                           // until conn_thread finishes.
    

    您还在 cmets 中提到,如果您不进行连接,那么您会被调用中止。这是因为线程的析构函数调用terminate(),如果它所代表的线程仍然是joinble

    由于您的线程对象是本地的,它在调用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()`
    

    那么你如何阻止它。

    1. 您可以调用detach()方法。
    2. 您可以使thread 属于更大的上下文,这样它就不会被破坏。

    致电detach() 不是一个好主意。当您失去对正在运行的线程的所有引用时,与它的通信变得困难。

    我还注意到您的class TCPClient 中有一个成员

    thread*               m_conn_thread;
    

    这个好像没用过。
    如果您将线程存储在此对象中,它将持续与对象一样长(因此比函数长)。自线程访问对象的成员以来,该对象显然应该与线程一样长。

    所以我会做出以下改变:

     // 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.
      }
    

    【讨论】:

    • 感谢您的详细解释!意识到我在另一个范围内初始化了线程后,我正在踢自己。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多