如何安全地停止STD线程?

12
我正在开发一个聊天服务器,有一个问题需要解决。
如何安全地停止 std::thread
这是一个非常简单的问题,像这样做即可。
thread t(&func);
t.join();

但是,如果函数有无限循环,使用join将无法工作。

这是我的源代码。

void CServerSocket::AcceptRun(boost::asio::io_service &iosrv)
{
    while (true)
    {
        auto sock = std::make_shared<boost::asio::ip::tcp::socket>(iosrv);
        m_Acceptor->accept(*sock);

        m_SocketList.push_back(std::make_shared<CConnectionSocket>(this, sock));
    }
}

and

CServerSocket::~CServerSocket()
{
    CLogManager::WriteLog("Stopping Server...");
    m_Acceptor->close();
    m_Acceptor.reset();

    // m_AcceptThread.detach(); This is right?

    CLogManager::WriteLog("Server Stoped!");
}

我很想知道。 请帮助我。 谢谢。


1
“join” 实际上运行得非常好。它有记录的阻塞行为并不意味着它是无法工作的东西。 - chris
1
我相当确定关闭接收器应该会导致对accept的调用抛出异常并退出循环。在加入线程之后再删除接收器。 - Mike Seymour
设置一个条件变量并加入,线程需要检查条件变量,如果被设置则清理并退出。 - Tim Seguine
Mike Seymour// 太棒了,谢谢你分享join()函数,非常完美!!! - BombPenguin
可能是重复的问题:Boost::asio - 如何中断阻塞的TCP服务器线程? - Mike Seymour
显示剩余2条评论
3个回答

9
您可以向线程传递一个合适的上下文,其中包含一个指示是否停止的标志。该标志可以是std::atomic<bool>。显然,您还需要建立通信以避免无限期地等待数据,这样您就有机会偶尔检查标志。

谢谢。但是,m_Acceptor.accept() 是阻塞模式。如果我调用该函数,它会阻塞。我该怎么办? - BombPenguin
@BombPenguin:祈祷。与在虚拟机中运行的托管语言不同,C++ 不允许您注入代码;因此,如果行为未编码(无论是由您还是您使用的库),则不会发生。由于您在这里使用 Boost,应该查看 acceptor 和 socket 的文档,并检查是否可以安全地从另一个线程发出信号以终止所有操作。 - Matthieu M.

3

我相信当你关闭接收器时,accept会干净地退出并抛出异常。您应该捕获异常,以便线程正常退出:

void CServerSocket::AcceptRun(boost::asio::io_service &iosrv)
try {
    // your loop here, unchanged
} catch (std::exception const & ex) {
    // perhaps log the message, ex.what()
}

在关闭 acceptor 之后但在销毁它之前,加入线程:
CServerSocket::~CServerSocket()
{
    CLogManager::WriteLog("Stopping Server...");
    m_Acceptor->close();
    m_AcceptThread.join();
    CLogManager::WriteLog("Server Stopped!");

    // No need to do anything else with m_Acceptor, assuming it's a smart pointer
}

个人认为,除非有强烈的理由需要使用多线程,否则应该使用异步操作。单个线程更容易处理。


3
如何安全地停止std::thread?
安全地停止线程意味着您通过一个在std :: thread之外的机制告诉线程函数停止处理,然后等待线程停止。 @DietmarKuhl的回应 告诉您如何做到这一点。关于acccept是阻塞的问题,您必须在套接字/接收器上设置一个选项,以在超时时过期。当accept调用返回时,您可以中断循环(如果循环条件为false),或者使用新的超时调用accept。
您的超时值将是一个折衷:小超时将更加计算密集(保持CPU繁忙),同时为您提供非常响应的线程函数(在停止线程时不会阻塞)。

2
在采取那种方法之前,您可能希望检查是否可以从另一个线程关闭套接字/完成接收器。 - Matthieu M.
我甚至没有考虑过这个(+1); 这是一个不错的解决方案,但是当查看代码时,我也不会考虑它,因为它在任何方面都不明确(所以我可能仍然会使用套接字或接受超时来实现)。 - utnapistim
在套接字中设置超时可能是个好主意,以避免无限期地挂起;关闭/终止的想法是(相对)立即的。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接