何时使用c ++类锁定互斥锁

时间:2013-11-25 12:42:25

标签: c++ multithreading mutex

我目前正致力于接受多个客户端的服务器。

在服务器端,我有一个线程池(手工制作,工作正常),可以在多个线程中进行午餐:

ThreadPool::bind(new TCPReceiver());
ThreadPool::bind(new TCPSender());

将某个类绑定到ThreadPool后,将调用其start()函数。

基本上我的服务器做的是:

  • 绑定线程
  • 接受一个或多个客户
  • 将客户端指针添加到TCPReceiver客户端列表
    • TCPReceiver执行socket.receive(),并将收到的数据推送到消息队列中的客户端
  • 将客户端指针添加到TCPSender客户端列表
    • TCPSender执行socket.send()并发送客户端的输出消息队列

因此,一旦客户端连接,其类的指针将附加到2个线程,一个读取套接字,一个发送套接字。虽然这一切,主线程(服务器)弹出客户端的输入消息队列。

class Server {
     std::list<Client*> clients;

     TCPReceiver receive;
     TCPSender   send;

     public:
     void *start();
}

class Client {
   std::list<NetworkMessage*> inQueue;
   IMutex *inMutex;

   std::list<NetworkMessage*> outQueue;
   IMutex *outMutex;

   Socket  *socket;
}

class TCPReceiver {
    std::list<Client*> clients;

     public:
     void *start();
}

class TCPSender {
    std::list<Client*> clients;

     public:
     void *start();
}

我的问题是:

从Server / TCPReceiver / TCPSender类,我可以在不锁定Client类的情况下访问/使用Client指针,但只能锁定Client的消息队列以弹出/推送它吗?

2个线程是否可以同时调用不同客户端的成员函数?

我可以在不锁定std :: list的情况下调用std :: list的成员函数(参见(* it) - &gt; inQueue.empty()调用)?

void Server::start() {
   for (std::list<Client*>::iterator it = this->clients.begin(); it != this->clients.end(); ++it) {
    if (!(*it)->inQueue.empty()) {
        (*it)->inMutex->lock();
        (*it)->inQueue.front();
        (*it)->inQueue.pop_front();
        (*it)->inMutex->unlock();
    }
   }
} 

同时在TCPReceiver上:

void TCPReceiver::start() {
    for (std::list<Client*>::iterator it = this->clients.begin(); it != this->clients.end(); ++it) {
        std::string msg = (*it)->socket->receive();
        if (!msg.empty()){ 
           (*it)->inMutex->lock();
           (*it)->inQueue.push_back(msg);
           (*it)->inMutex->unlock();
        }
    }
}

(我知道套接字也应该有一个互斥锁,但这不是我现在想要解决的问题)

1 个答案:

答案 0 :(得分:1)

是的,两个线程确实可以同时执行同一个实例的方法。您需要某种同步机制来防止因同时修改相同值而导致的竞争条件。在这方面,读取并不比写入更危险,因为在写入操作正在进行时读取可能会导致读取垃圾值。

基本上,这意味着您应该在检查队列是否为空之前锁定(因为您的线程可能会在该行和下一行之间暂停),但您不需要锁定在循环之外迭代客户端,只要保证客户端列表在迭代期间不会改变。

您需要确保没有线程在其处于无效状态时访问任何对象。对于像您所描述的那样的生产者 - 消费者类型的情况,您可能有兴趣了解condition variables,它提供了等待某个状态改变的方法。 (例如,等待空队列不再为空)。

请注意,在您提供的示例中,您只需循环clients一次,并从/向每个队列添加/删除最多一条消息。