muduo库的线程安全问题
什么是线程安全
所谓线程安全是指凡是非共享的对象都是彼此独立的,如果一个对象从始至终只被一个线程用到,那么它就是安全的。共享的对象的read-only操作是安全的,前提是不能有并发的写操作。(如果有并发的写操作那么将会是线程不安全的),同样在多线程C++中还要注意多个线程如果都可以访问一个对象时的对象析构问题,所以对于这样的对象最好采取智能指针去管理。所以说在并发服务器里,对于同一个连接是不能够供多个线程同时进行读或者写,所以muduo库规定所有的读写操作只能在IO线程(包括连接关闭操作)中来完成。muduo库通过runInLoop和queueInLoop这两个函数来保证将可能在其他线程执行的IO操作放入到IO线程来完成。(比如在线程池中的线程可能会用到send操作或者close操作)
muduo规定的必须在IO线程完成的操作:
连接的更新
连接的读写
连接的关闭
连接的析构等
- TcpServer::start():将Acceptor::listen置于所属线程?
一直不理解为什么这里需要将listen放到其所属线程中(因为listen的描述符一定是在IO线程中的)可能原因是其他从reactor线程中可能会创建监听描述符
- TcpServer::newConnection():将TcpConnection::connectEstablished置于所属线程
这是肯定的,因为connectEstablished函数涉及连接的更新操作,这个函数应该由其连接所属的线程来调用,而此时的函数是监听fd所属的线程所以需要采取此操作。
- TcpServer::removeConnection:将removeConnectionInLoop置于所属线程
为了避免在删除map中的conn对象时出现竞争,muduo规定只能在map(也就是监听fd)所属的线程中进行删除操作,从而避免了多个线程使用map而造成的竞争。
- TcpServer::removeConnectionInLoop:将TcpConnection::connectDestroyed置于queueInLoop
此操作根本是保证channel不会在TcpConnection对象析构后而析构,因为handleRead等操作是由channel直接调用的,但是此函数可能会直接将channel析构调用从而出现段错误,所以采用此操作使得channel的析构推迟。
- TcpConnection::send:将TcpConnection::sendInLoop置于所属线程
因为线程池的线程可能会采用send操作
- TcpConnection::shutdown将TcpConnection::shutdownInLoop置于所属线程
因为线程池中的线程可能会采用shutdown操作
- TcpServer::~TcpServer将TcpConnection::connectionDestroy置于所属线程
因为TcpServer在析构时需要关闭所有的连接,而析构函数是在监听fd所属的IO线程的,所有要采取此操作。
- TcpConnection::forceClose将TcpConnection::forceCloseInLoop置于所属线程
因为线程池中的线程可能会采用forceClose操作