【问题标题】:Crash : terminate called after throwing an instance of 'std::system_error' what(): Resource deadlock avoided崩溃:在抛出 'std::system_error' what() 实例后调用终止:避免了资源死锁
【发布时间】:2018-06-13 01:12:35
【问题描述】:

我有一个简单的客户端/服务器应用程序,其代码如下所述。 请在 linux 的一个 shell 中运行服务器,在另一个 shell 中运行客户端。 首先启动服务器,然后启动客户端。 当服务器完成它的工作时,它会崩溃并出现以下异常:

在抛出 'std::system_error' 实例后调用终止 what():避免了资源死锁

这发生在函数 Service::HandleClient 内部的 m_thread->join() 行 我不知道发生了什么..有人可以检查代码..我只是希望服务器应用程序也应该像关闭客户端应用程序一样正确关闭。

**服务器代码:**

#include <boost/asio.hpp>

#include <thread>
#include <atomic>
#include <memory>
#include <iostream>

using namespace boost;

class Service {
public:
    Service(){}

    void StartHandligClient(
        std::shared_ptr<asio::ip::tcp::socket> sock) {

        m_thread.reset(new std::thread (([this, sock]() {
            HandleClient(sock);
        })) );

    }

private:
    void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
        while(1)
        {
            try {
                asio::streambuf request;
                std::cout << "Waiting to read \n";
                asio::read_until(*sock.get(), request, '\n');

                std::string s( (std::istreambuf_iterator<char>(&request)), std::istreambuf_iterator<char>() );
                std::cout << "Server got : " << s << "\n";

                // Emulate request processing.
                int i = 0;
                while (i != 1000000)
                    i++;

                std::this_thread::sleep_for(
                        std::chrono::milliseconds(500));

                // Sending response.
                std::string response = "Response\n";
                asio::write(*sock.get(), asio::buffer(response));
            }
            catch (system::system_error &e) {
                boost::system::error_code ec = e.code();
                if(ec == asio::error::eof)
                {
                    std::cout << "Breaking loop \n";
                    break;
                }
                std::cout << "Error occured! Error code = "
                    << e.code() << ". Message: "
                    << e.what();
            }
        }

        m_thread->join();

        // Clean-up.
        delete this;
    }
    std::unique_ptr<std::thread> m_thread;
};

class Acceptor {
public:
    Acceptor(asio::io_service& ios, unsigned short port_num) :
        m_ios(ios),
        m_acceptor(m_ios,
        asio::ip::tcp::endpoint(
        asio::ip::address_v4::any(),
        port_num))
    {
        m_acceptor.listen();
    }

    void Accept() {
        std::cout << "Server Accept() \n" << std::flush;
        std::shared_ptr<asio::ip::tcp::socket>
            sock(new asio::ip::tcp::socket(m_ios));

        std::cout << "BEFORE calling acceptor's accept function \n" << std::flush;
        m_acceptor.accept(*sock.get());
        std::cout << "AFTER calling acceptor's accept function \n" << std::flush;


        (new Service)->StartHandligClient(sock);
    }

    void close()
    {
        std::cout << "Inside Acceptor.close() \n" << std::flush;
        m_acceptor.close();
    }

private:
    asio::io_service& m_ios;
    asio::ip::tcp::acceptor m_acceptor;
};

class Server {
public:
    Server() : m_stop(false) {}

    void Start(unsigned short port_num) {
        m_thread.reset(new std::thread([this, port_num]() {
            Run(port_num);
        }));
    }

    void Stop() {
        m_stop.store(true);
        m_thread->join();
    }

private:
    void Run(unsigned short port_num) {
        Acceptor acc(m_ios, port_num);

        while (!m_stop.load()) {
            std::cout << "Server accept\n" << std::flush;
            acc.Accept();
        }
        acc.close();
    }

    std::unique_ptr<std::thread> m_thread;
    std::atomic<bool> m_stop;
    asio::io_service m_ios;
};

int main()
{
    unsigned short port_num = 3333;

    try {
        Server srv;
        srv.Start(port_num);

        std::this_thread::sleep_for(std::chrono::seconds(4));

        srv.Stop();
    }
    catch (system::system_error &e) {
        std::cout << "Error occured! Error code = "
            << e.code() << ". Message: "
            << e.what();
    }

    return 0;
}

**客户端代码:**

#include <boost/asio.hpp>
#include <iostream>

using namespace boost;

class SyncTCPClient {
public:
    SyncTCPClient(const std::string& raw_ip_address,
        unsigned short port_num) :
        m_ep(asio::ip::address::from_string(raw_ip_address),
        port_num),
        m_sock(m_ios) {

        m_sock.open(m_ep.protocol());
    }

    void connect() {
        m_sock.connect(m_ep);
    }

    void close() {
        m_sock.shutdown(
            boost::asio::ip::tcp::socket::shutdown_both);
        m_sock.close();
    }

    std::string emulateLongComputationOp(
        unsigned int duration_sec) {

        std::string request = "EMULATE_LONG_COMP_OP "
            + std::to_string(duration_sec)
            + "\n";

        sendRequest(request);
        return receiveResponse();
    };

private:
    void sendRequest(const std::string& request)
    {
        std::cout << "Inside sendRequest : " << request << "\n";
        asio::write(m_sock, asio::buffer(request));
    }

    std::string receiveResponse() {
        asio::streambuf buf;
        asio::read_until(m_sock, buf, '\n');

        std::istream input(&buf);

        std::string response;
        std::getline(input, response);

        return response;
    }

private:
    asio::io_service m_ios;

    asio::ip::tcp::endpoint m_ep;
    asio::ip::tcp::socket m_sock;
};

int main()
{
    const std::string raw_ip_address = "127.0.0.1";
    const unsigned short port_num = 3333;

    try {
        SyncTCPClient client(raw_ip_address, port_num);

        // Sync connect.
        client.connect();

        std::cout << "Sending request to the server... " << std::endl;
        std::string response = client.emulateLongComputationOp(10);

        std::cout << "Response received: " << response << std::endl;
        sleep(2);

        // Close the connection and free resources.
        client.close();
    }
    catch (system::system_error &e) {
        std::cout << "Error occured! Error code = " << e.code()
            << ". Message: " << e.what();

        return e.code().value();
    }

    return 0;
}

【问题讨论】:

标签: c++ boost boost-asio


【解决方案1】:

@sehe .. 你能运行代码并告诉我如何克服我提到的崩溃吗? – 尼山特·夏尔马

实际上,不,我不会。问题已经分析过了:不能join当前线程(会死锁)。

但我可以做得更好:

拿起我的水晶球,我猜你是从一本名为 Boost.Asio C++ Network Programming Cookbook¹ 的特定书中得到的这个例子,大约在第 139 页。

一段时间后,当我把所有的代码气味加起来时,我就认出了它(delete thism_stop.load() 让我大吃一惊)。

好消息是,我之前查看过该代码:

ASIO example code closing socket before it should

您可能可以从我在那里制作的特定 cmets 中获利。

¹来自 packtpub:https://www.packtpub.com/application-development/boostasio-c-network-programming-cookbook

【讨论】:

  • 谢谢@sehe 我还有 2 个问题:a) 我仍然看到服务器应用程序在运行时没有结束,我必须按 Ctrl-C 才能做到这一点。我希望服务器应用程序能够干净地退出... b) 在线程上使用 detach() 通常不是一个好习惯。不是吗?我们应该总是在最后加入一个线程。那么,我们可以摆脱那个 detach() 吗?
  • 分离不是问题(您可以使用join以外的其他方法同步线程完成)。在程序退出时出现“失控”线程或未完成的线程是问题所在。
  • 关于关机,你没有办法取消同步接受。您可以尝试(a)提升线程中断点(不认为/不知道这会起作用)(b)从服务器本身连接以确保接受返回
  • 这是最后一个丑陋解决方法的概念验证:coliru.stacked-crooked.com/a/ef5cc6c3b0b494dc
  • 概念证明看起来不错。我喜欢连接到自身的想法。代码在第53行有问题..应该是在if(joinable())的条件下,因为分离的线程无法加入。你同意吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 2017-09-26
相关资源
最近更新 更多