从套接字和 STDIN 提升 Asio 多线程

Posted

技术标签:

【中文标题】从套接字和 STDIN 提升 Asio 多线程【英文标题】:Boost Asio Multithreading From socket and STDIN 【发布时间】:2013-01-23 08:18:26 【问题描述】:

我只想拥有一个聊天客户端,它异步监听套接字和分配给标准输入的boost::asio::posix::stream_descriptor

如果我在单线程应用程序中运行此代码,一切正常。

如果我从 2 个或更多线程调用 io_service.run(),来自标准输入的异步操作永远不会正常,但仍然执行来自套接字的异步读取。

代码如下:

MasterClient::MasterClient(boost::asio::io_service& io_service,boost::asio::ip::tcp::resolver::iterator iter, string nickName)
:it(iter),chatNick(nickName)

    this->fdIn_ = str_ptr(new boost::asio::posix::stream_descriptor(io_service,::dup(STDIN_FILENO)));
    this->dirServer_ = new(connectedPeer);
    this->dirServer_->sock = socket_ptr(new boost::asio::ip::tcp::socket(this->io_service_));
    boost::asio::async_connect(*(this->dirServer_->sock), this->it,
        boost::bind(&MasterClient::connectionHandler, this,
                boost::asio::placeholders::error));

主要:

int main(int argc, const char * argv[])

boost::asio::io_service io_service(2);
    boost::asio::io_service::work work(io_service);
    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::resolver::query query(argv[1], argv[2]);
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
    string nick;
    cout << "Inserire un nickname per la sessione di chat: " << flush;
    getline(cin,nick);
    MasterClient cli(io_service,iterator,nick);
cli.run();

和 MasterClient::run()

void MasterClient::run()

// Create a pool of threads to run all of the io_services.
std::vector<boost::shared_ptr<boost::thread> > threads;
boost::asio::io_service::work work(this->io_service_);
for (std::size_t i = 0; i < 1; ++i)

    boost::shared_ptr<boost::thread> thread(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_)));
    threads.push_back(thread);



// Wait for all threads in the pool to exit.
for (std::size_t i = 0; i < threads.size(); ++i)
    threads[i]->join();

这里我称之为异步读数:

void MasterClient::clientDelegate()

if(this->connectToServer())

    this->mainMenu();

    boost::asio::async_read_until(*fdIn_, inBuff_, '\n',
            boost::bind(&MasterClient::fdInMenuHandler,
                    this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

    (*(this->dirServer_->sock)).async_read_some(boost::asio::buffer(this->buff_),
            boost::bind(&MasterClient::serverHandler,
                    this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred,
                    this->dirServer_->sock));
    this->io_service_.post(boost::bind(&MasterClient::printer,this));
else

    if(this->isDebugging)
        cout << "Error in ClientDelegate." << endl;

if(this->isDebugging)
    cout << "ClientDelegate END" << endl;

连接处理程序,调用clientDelegate:

void MasterClient::connectionHandler(const boost::system::error_code& error)

cout << "connected" << endl;
try

    if(error)
        throw boost::system::system_error(error);
    else
    
        this->dirServer_->endpoint = boost::asio::ip::tcp::endpoint((*(this->dirServer_->sock)).remote_endpoint());
        this->clientDelegate();
    
catch(const boost::system::system_error& e)

    cerr << "Boost Exception in ConnectHandler ---> " << e.what() << endl;
    this->io_service_.stop();


我做错了什么?

【问题讨论】:

【参考方案1】:

根据boost::asio::posix::stream_descriptor 的文档,在多个线程中使用同一个实例是不安全的。在多线程情况下,通常将处理程序包装到一个链中以进行序列化。

在您的情况下,我看不出为同一个客户端连接使用多个线程的意义。

【讨论】:

根据 boost asio 文档,如果我将 io_service.run 调用到多个线程中,这些线程就有资格调用处理程序。我错了吗? 是的,从多个线程调用同一个io_service的run方法是安全的。但是,处理程序可以同时调用,因此您需要小心。 Asio 提供了 strands 来帮助您在多线程程序中序列化处理程序。 尚不清楚您的处理程序链在您的程序中的位置。 clientDelegate 在哪里调用? fdInMenuHandler 是做什么的?将多线程与单个客户端连接一起使用看起来很奇怪。为什么不是单线程? 它是多线程的,因为客户端有 3 个套接字。 1个socket用于传输文本,2个socket用于传输文件。所以想法是使用 3 个线程进行连接,以同时处理 3 个线程。 clientDelegate 在连接到服务器后作为第一个方法被调用。它在连接处理程序中调用。我刚刚在问题的帖子中添加了代码。问题不在于并发。但是 fdInMenuHandler 永远不会被调用的事实。 fdInMenuHandler 解析用户输入并做一些事情,就像菜单一样。

以上是关于从套接字和 STDIN 提升 Asio 多线程的主要内容,如果未能解决你的问题,请参考以下文章

C++ 提升 asio 多线程

Boost::asio - 如何中断阻塞的 tcp 服务器线程?

如何使用 asio 简化多线程设计?

在给定线程上使用同步 I/O 提升 asio 多线程 tcp 服务器

提升 Asio Peek 和完成条件

Python 多线程效率不高吗