C++ boost::asio::套接字线程安全

C++ boost::asio::套接字线程安全,c++,boost,boost-asio,C++,Boost,Boost Asio,(这是我原来问题的简化版本) 我有几个线程可以写入boost asio套接字。这似乎工作得很好,没有问题 文档中说共享套接字不是线程安全的(位于底部),所以我想知道是否应该使用互斥锁或其他什么来保护套接字 这坚持认为保护是必要的,但没有给出如何保护的建议 我最初问题的所有答案都坚持认为我所做的事情是危险的,大多数人都敦促我用异步_写或更复杂的事情来取代我的写操作。然而,我不愿意这样做,因为这会使已经在运行的代码复杂化,而且没有一个回答者让我相信他们知道他们在谈论什么——他们似乎读过与我相同的文档

(这是我原来问题的简化版本)

我有几个线程可以写入boost asio套接字。这似乎工作得很好,没有问题

文档中说共享套接字不是线程安全的(位于底部),所以我想知道是否应该使用互斥锁或其他什么来保护套接字

这坚持认为保护是必要的,但没有给出如何保护的建议

我最初问题的所有答案都坚持认为我所做的事情是危险的,大多数人都敦促我用异步_写或更复杂的事情来取代我的写操作。然而,我不愿意这样做,因为这会使已经在运行的代码复杂化,而且没有一个回答者让我相信他们知道他们在谈论什么——他们似乎读过与我相同的文档,并且在猜测,就像我一样

因此,我编写了一个简单的程序,对从两个线程写入共享套接字进行压力测试

这里是服务器,它只是写出它从客户机接收到的任何内容

int main()
{
    boost::asio::io_service io_service;

    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 3001));

    tcp::socket socket(io_service);
    acceptor.accept(socket);

    for (;;)
    {
        char mybuffer[1256];
        int len = socket.read_some(boost::asio::buffer(mybuffer,1256));
        mybuffer[len] = '\0';
        std::cout << mybuffer;
        std::cout.flush();

    }

  return 0;
}
intmain()
{
boost::asio::io_服务io_服务;
tcp::acceptor-acceptor(io_服务,tcp::endpoint(tcp::v4(),3001));
tcp::套接字(io_服务);
接受(套接字);
对于(;;)
{
char-mybuffer[1256];
int len=socket.read_some(boost::asio::buffer(mybuffer,1256));
mybuffer[len]='\0';
std::cout close();
psocket->connect(*endpoint_iterator++,error);
}
boost::线程t1(扬声器1);
boost::线程t2(扬声器2);
睡眠(50000);
}
这管用!就我所知,这是完美的。客户端不会崩溃。消息到达服务器时不会出现乱码。它们通常交替到达,每根线一个。有时一个线程在另一个线程之前收到两到三条消息,但我认为这不是一个问题,只要没有乱码并且所有消息都到达

我的结论是:从某种理论上讲,套接字可能不是线程安全的,但要使它失败太难了,我不必担心它。

对于非线程安全的异步处理程序,请使用

串被定义为事件的严格顺序调用 处理程序(即没有并发调用)。使用绞线允许 在多线程程序中执行代码而不需要 显式锁定(例如使用互斥锁)


这可能是将您的头缠绕在股线上的最简单方法。

这取决于您是否从多个线程访问同一个套接字对象。假设有两个线程运行相同的
io\u service::run()
函数

例如,如果您同时进行读写操作,或者可能需要执行取消操作 从另一个线程。那就不安全了

但是,如果您的协议一次只执行一个操作

  • 如果只有一个线程运行io_服务,那么就没有问题。如果要从其他线程在套接字上执行某些操作,可以使用 在套接字上执行此操作以便在同一线程中执行的处理程序
  • 如果您有多个线程在执行
    io_service::run
    ,并且您尝试同时执行操作,比如取消和读取操作,那么您应该使用线程。Boost.Asio文档中有一个关于此的教程

  • 理解ASIO的关键是要认识到,无论哪个线程调用异步方法,完成处理程序都只在调用了
    io\u service.run()
    的线程的上下文中运行。如果您只在一个线程中调用了
    io\u service.run()
    ,那么所有完成处理程序都将在该线程的上下文中串行执行。如果在多个线程中调用了
    io\u service.run()
    ,则完成处理程序将在其中一个线程的上下文中执行。您可以将其视为一个线程池,其中池中的线程是在同一
    io\u服务
    对象上调用
    io\u service.run()
    的线程

    如果有多个线程调用
    io\u service.run()
    ,则可以通过将完成处理程序放入
    串中来强制序列化它们


    要回答问题的最后一部分,您应该调用
    boost::async\u write()
    。这将把写操作分派到调用了
    io\u service.run()
    的线程上,并在写操作完成时调用完成处理程序。如果您需要序列化此操作,那么它会稍微复杂一些,您应该阅读有关绞线的文档。

    听起来这个问题可以归结为:

    当从两个不同线程在单个套接字上同时调用
    async\u write\u some()
    时会发生什么情况

    我相信这正是线程不安全的操作。这些缓冲区在线路上的输出顺序尚未定义,甚至可能是交错的。特别是如果您使用便利函数
    async\u write()
    ,因为它是作为对下面的
    async\u write\u some()
    的一系列调用来实现的,直到发送了整个缓冲区。在这种情况下,从两个线程发送的每个片段可能会随机交错

    保护您不受这种情况影响的唯一方法是构建您的程序以避免这种情况


    一种方法是编写一个应用层发送缓冲区,由一个线程负责推送到套接字上。这样,您可以只保护发送缓冲区本身。但是请记住,一个简单的
    std::vector
    是行不通的,因为在末尾添加字节可能会导致重新分配它,可能是因为有一个未完成的
    async\u write\u some()
    引用它。相反,使用缓冲区链接列表,并利用asio的分散/聚集功能可能是一个好主意。

    根据2008年11月boost 1.37 asio更新,确定
    boost::asio::ip::tcp::socket * psocket;
    
    void speaker1()
    {
        string msg("speaker1: hello, server, how are you running?\n");
        for( int k = 0; k < 1000; k++ ) {
            boost::asio::write(
                *psocket,boost::asio::buffer(msg,msg.length()));
        }
    
    }
    void speaker2()
    {
        string msg("speaker2: hello, server, how are you running?\n");
        for( int k = 0; k < 1000; k++ ) {
            boost::asio::write(
                *psocket,boost::asio::buffer(msg,msg.length()));
        }
    
    }
    
    int main(int argc, char* argv[])
    {
    
        boost::asio::io_service io_service;
    
      // connect to server
    
        tcp::resolver resolver(io_service);
        tcp::resolver::query query("localhost", "3001");
        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        tcp::resolver::iterator end;
        psocket = new tcp::socket(io_service);
        boost::system::error_code error = boost::asio::error::host_not_found;
        while (error && endpoint_iterator != end)
        {
            psocket->close();
            psocket->connect(*endpoint_iterator++, error);
        }
    
    
        boost::thread t1( speaker1 );
        boost::thread t2( speaker2 );
    
        Sleep(50000);
    
    }
    
    #include <boost/thread.hpp>
    #include <boost/date_time.hpp>
    #include <boost/asio.hpp>
    #include <vector>
    
    using namespace std;
    char databuffer[256];
    vector<boost::asio::const_buffer> scatter_buffer;
    boost::mutex my_test_mutex;
    void my_test_func(boost::asio::ip::tcp::socket* socket, boost::asio::io_service *io) {
    while(1) {
        boost::this_thread::sleep(boost::posix_time::microsec(rand()%1000));
    
        //my_test_mutex.lock(); // It would be safer 
        socket->async_send(scatter_buffer, boost::bind(&mycallback));
        //my_test_mutex.unlock(); // It would be safer
    }
    }
    int main(int argc, char **argv) {
    
    for(int i = 0; i < 256; ++i)
        databuffer[i] = i;
    
    for(int i = 0; i < 4*90; ++i)
        scatter_buffer.push_back(boost::asio::buffer(databuffer));
    boost::asio::io_service my_test_ioservice;
    boost::asio::ip::tcp::socket my_test_socket(my_test_ioservice);
    boost::asio::ip::tcp::resolver my_test_tcp_resolver(my_test_ioservice);
    boost::asio::ip::tcp::resolver::query  my_test_tcp_query("192.168.1.10", "40000");
    boost::asio::ip::tcp::resolver::iterator my_test_tcp_iterator = my_test_tcp_resolver.resolve(my_test_tcp_query);
    boost::asio::connect(my_test_socket, my_test_tcp_iterator);
    for (size_t i = 0; i < 8; ++i) {
        boost::shared_ptr<boost::thread> thread(
                new boost::thread(my_test_func, &my_test_socket, &my_test_ioservice));
    }
    
    while(1) {
        my_test_ioservice.run_one();
        boost::this_thread::sleep(boost::posix_time::microsec(rand()%1000));
    }
    return 0;
    
    import socket
    def main():
        mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        mysocket.bind((socket.gethostname(), 40000))
        mysocket.listen(1)
    
        while 1:
            (clientsocket, address) = mysocket.accept()
            print("Connection from: " + str(address))
            i = 0
            count = 0
            while i == ord(clientsocket.recv(1)):
                i += 1
                i %= 256
    
                count+=1
                if count % 1000 == 0:
                    print(count/1000)
            print("Error!")
    return 0
    
    if __name__ == '__main__':
        main()
    
    default_max_transfer_size = 65536;
    
        void my_connection::async_serialized_write(
                boost::shared_ptr<transmission> outpacket) {
            m_tx_mutex.lock();
            bool in_progress = !m_pending_transmissions.empty();
            m_pending_transmissions.push(outpacket);
            if (!in_progress) {
                if (m_pending_transmissions.front()->scatter_buffers.size() > 0) {
                    boost::asio::async_write(m_socket,
                        m_pending_transmissions.front()->scatter_buffers,
                            boost::asio::transfer_all(),
                boost::bind(&my_connection::handle_async_serialized_write,
                            shared_from_this(),
                            boost::asio::placeholders::error,
                                           boost::asio::placeholders::bytes_transferred));
                } else { // Send single buffer
                    boost::asio::async_write(m_socket,
                                        boost::asio::buffer(
                                               m_pending_transmissions.front()->buffer_references.front(),                          m_pending_transmissions.front()->num_bytes_left),
                    boost::asio::transfer_all(),
                    boost::bind(
                            &my_connection::handle_async_serialized_write,
                            shared_from_this(),
                            boost::asio::placeholders::error,
                            boost::asio::placeholders::bytes_transferred));
                }
            }
            m_tx_mutex.unlock();
        }
    
        void my_connection::handle_async_serialized_write(
        const boost::system::error_code& e, size_t bytes_transferred) {
            if (!e) {
                boost::shared_ptr<transmission> transmission;
                m_tx_mutex.lock();
                transmission = m_pending_transmissions.front();
                m_pending_transmissions.pop();
                if (!m_pending_transmissions.empty()) {
                    if (m_pending_transmissions.front()->scatter_buffers.size() > 0) {
                boost::asio::async_write(m_socket,
                        m_pending_transmissions.front()->scatter_buffers,
                        boost::asio::transfer_exactly(
                                m_pending_transmissions.front()->num_bytes_left),
                        boost::bind(
                                &chreosis_connection::handle_async_serialized_write,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred));
                    } else { // Send single buffer
                        boost::asio::async_write(m_socket,
                        boost::asio::buffer(
                                m_pending_transmissions.front()->buffer_references.front(),
                                m_pending_transmissions.front()->num_bytes_left),
                        boost::asio::transfer_all(),
                        boost::bind(
                                &my_connection::handle_async_serialized_write,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred));
                    }
                }
                m_tx_mutex.unlock();
                transmission->handler(e, bytes_transferred, transmission);
            } else {
                MYLOG_ERROR(
                m_connection_oid.toString() << " " << "handle_async_serialized_write: " << e.message());
                stop(connection_stop_reasons::stop_async_handler_error);
            }
        }
    
    /**
     * This constructor creates a stream socket without opening it. The socket
     * needs to be opened and then connected or accepted before data can be sent
     * or received on it.
     *
     * @param io_service The io_service object that the stream socket will use to
     * dispatch handlers for any asynchronous operations performed on the socket.
     */
    explicit basic_stream_socket(boost::asio::io_service& io_service)
    : basic_socket<Protocol, StreamSocketService>(io_service)
    {
    }
    
    /**
     * The run() function blocks until all work has finished and there are no
     * more handlers to be dispatched, or until the io_service has been stopped.
     *
     * Multiple threads may call the run() function to set up a pool of threads
     * from which the io_service may execute handlers. All threads that are
     * waiting in the pool are equivalent and the io_service may choose any one
     * of them to invoke a handler.
     *
     * ...
     */
    BOOST_ASIO_DECL std::size_t run();