从线程本身安全地停止线程

问题描述 投票:0回答:1

我有一个包裹winsock的对象,这是一个简化版:

struct Listener
{
    using accept_callback = void(*)(Client &&);

    ~Listener() { WSACleanup(); }

    Listener(const std::basic_string<TCHAR> &a_ip, std::uint16_t a_port, accept_callback a_callback)
    {
        // Error control ommited for brevity

        m_on_connection_accepted = a_callback;
        WSADATA sockets_data{};
        WSAStartup(MAKEWORD(2, 2), &sockets_data);

        m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        sockaddr_in service { .sin_family = AF_INET, .sin_port = htons(a_port) };
        InetPton(service.sin_family, a_ip.data(), &service.sin_addr);

        bind(m_socket, reinterpret_cast<SOCKADDR *>(&service), sizeof(service));
        listen(m_socket, SOMAXCONN);

        m_listen_thread = std::jthread([&]
        {
            while (m_socket != INVALID_SOCKET)
            {
                if (SOCKET client = accept(m_socket, NULL, NULL); client == INVALID_SOCKET)
                    break;
                else
                    m_on_connection_accepted(std::move(Client{ client }));
            }
        });

    }

    void stop()
    {
        closesocket(m_socket);
        m_socket = INVALID_SOCKET;

        if (m_listen_thread.joinable())
            m_listen_thread.join();

        m_listen_thread = {};
    }

private:

    accept_callback m_on_connection_accepted{ nullptr };
    SOCKET m_socket{ INVALID_SOCKET };
    std::jthread m_listen_thread{};
};

我的目标是在客户端连接后立即停止监听器,这样一次连接的客户端不会超过一个:

void on_client_connected(sockets::Client &&a_new_client)
{
    // … do things …

    m_listener.stop();
}

此代码会导致

abort()
调用,因为接受客户端会导致监听线程从线程本身的调用中加入:thread loop
m_on_connection_accepted(…)
on_client_connected(…)
Listener::stop()
jthread::join()
.

我以为结果会是:

  1. m_socket
    设置为
    INVALID_SOCKET
    .
  2. 线程连接。
  3. 线程在
    m_on_connection_accepted
    调用后继续运行。
  4. 不满足线程循环条件
  5. Trhead 完成。

很明显我错了,我应该怎么做才能达到我的目标?

c++ multithreading
1个回答
0
投票

如果我一次只想为一个客户服务,我根本不会创建一个线程来为客户服务。

看来你想要的是这样一个序列:

for (;;) {
    auto client = listen_for_client_connection();
    service(client);
}

在一个线程中监听并在另一个线程中为客户端提供服务的全部意义在于,当一个客户端连接时,您可以为该客户端提供服务,立即监听新连接,因此您可以同时为多个客户端提供服务。

既然您一次只想为一个客户提供服务,那么……就不要做任何专门为多个并发客户提供服务的“事情”。由于您一次只想处理一个客户端,因此您只需要一个执行线程。

© www.soinside.com 2019 - 2024. All rights reserved.