我目前正致力于接受多个客户端的服务器。
在服务器端,我有一个线程池(手工制作,工作正常),可以在多个线程中进行午餐:
ThreadPool::bind(new TCPReceiver());
ThreadPool::bind(new TCPSender());
将某个类绑定到ThreadPool
后,将调用其start()
函数。
基本上我的服务器做的是:
socket.receive()
,并将收到的数据推送到消息队列中的客户端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();
}
}
}
(我知道套接字也应该有一个互斥锁,但这不是我现在想要解决的问题)
答案 0 :(得分:1)
是的,两个线程确实可以同时执行同一个实例的方法。您需要某种同步机制来防止因同时修改相同值而导致的竞争条件。在这方面,读取并不比写入更危险,因为在写入操作正在进行时读取可能会导致读取垃圾值。
基本上,这意味着您应该在检查队列是否为空之前锁定(因为您的线程可能会在该行和下一行之间暂停),但您不需要锁定在循环之外迭代客户端,只要保证客户端列表在迭代期间不会改变。
您需要确保没有线程在其处于无效状态时访问任何对象。对于像您所描述的那样的生产者 - 消费者类型的情况,您可能有兴趣了解condition variables,它提供了等待某个状态改变的方法。 (例如,等待空队列不再为空)。
请注意,在您提供的示例中,您只需循环clients
一次,并从/向每个队列添加/删除最多一条消息。