求解惑:关于多线程并发环境下构造函数的线程安全

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求解惑:关于多线程并发环境下构造函数的线程安全相关的知识,希望对你有一定的参考价值。

在构造期间不要泄露 this 指针

对象构造要做到线程安全,惟一的要求是在构造期间不要泄露 this 指针,即

1、不要在构造函数中注册任何回调
2、也不要在构造函数中把 this 传给跨线程的对象
3、即便在构造函数的最后一行也不行

之所以这样规定,是因为在构造函数执行期间对象还没有完成初始化,如果 this 被泄露 (escape) 给了其他对象(其自身创建的子对象除外),那么别的线程有可能访问这个半成品对象,这会造成难以预料的后果。

来自:https://www.cnblogs.com/Vancamel/p/11316410.html


这就完了吗?这个自然是好理解的,有什么好疑惑的。


我的“犯忌”代码以及诡辩

下面是我的,说实话,写这个的时候不知道这个构造函数的线程安全。

ChatServer::ChatServer(EventLoop *loop,
                       const InetAddress &listenAddr,
                       const string &nameArg) : _server(loop, listenAddr, nameArg),
                                                _loop(loop)
{
    //注册连接回调
    _server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));

    //注册消息回调
    _server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));

    //设置线程数
    _server.setThreadNum(5);
}
//注册消息以及对应的回调操作
ChatService::ChatService(){
    _msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)});
    _msgHanderMap.insert({REG_TYPE,std::bind(&ChatService::reg,this,_1,_2,_3)});
    _msgHanderMap.insert({ONE_CHAT_MSG,std::bind(&ChatService::onechat,this,_1,_2,_3)});
    _msgHanderMap.insert({ADD_FRINEND_MSG,std::bind(&ChatService::addFriend,this,_1,_2,_3)});
 
    // 群组业务管理相关事件处理回调注册
    _msgHanderMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
    _msgHanderMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
    _msgHanderMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});

    // 连接redis服务器
    if (_redis.connect()){
        // 设置上报消息的回调
        _redis.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, _1, _2));
    }
}

且听我狡辩一下:

这俩类对象呐,在服务器开始运行的时候就被构造了对象,所以我可以确定它们在对象构造完成之前这个回调是不会被触发的。
甚至我还可以调控在这俩对象被构造之后好久都不会被触发。

(那是自然,只要我不连客户端,怎么触发?哈哈哈哈)


我解释不了的代码

那如果有的对象在服务器运行过程中要被频繁调用呢?那我真的不知道该怎么解释了,就有点疑惑。

能解释的我就不拿出来展示了,我展示一下我解释不了的,希望有大佬看到在评论区帮我解惑吧,谢谢

TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    reading_(true),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    localAddr_(localAddr),
    peerAddr_(peerAddr),
    highWaterMark_(64*1024*1024)
{
  channel_->setReadCallback(
      std::bind(&TcpConnection::handleRead, this, _1));
  channel_->setWriteCallback(
      std::bind(&TcpConnection::handleWrite, this));
  channel_->setCloseCallback(
      std::bind(&TcpConnection::handleClose, this));
  channel_->setErrorCallback(
      std::bind(&TcpConnection::handleError, this));
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  socket_->setKeepAlive(true);
}

这个 TCPConnection 可是和新连接的 fd 一一对应啊,来一个新连接就new一个TCPConnection,然后立刻就绑定channel,扔EventLoop里面去了,用poller监控起来了。

这我就不知道该怎么解释了。。。

(下面这段代码出自muduo库 TCPConnection.cc)

以上是关于求解惑:关于多线程并发环境下构造函数的线程安全的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式环境下并发控制与线程安全

Java并发编程:多线程环境中安全使用集合API(含代码)

List集合多线程并发条件下不安全,如何解决?

Spring如何处理线程并发问题

转:Java并发编程之八:多线程环境中安全使用集合API(含代码)

高并发基石多线程守护线程线程安全线程同步互斥锁