我正在使用C++和websockets开发一个P2P库,目前我正在实现一种对节点进行异步请求的方法,给定以下类:
class Request {
private:
CommandType _command; // The command type.
std::string _id; // The request id.
std::string _nodeId; // The host node id.
Message _answer; // The answer to the request.
std::mutex _mutex; // Mutex for waiting for the request.
bool _isAnswered = false;
void release() { _mutex.unlock(); };
public:
Request(const CommandType& command, const std::string& id, const std::string& nodeId) : _command(command), _id(id), _nodeId(nodeId) {};
const CommandType command() const { return _command; };
const std::string_view id() const { return _id; };
const std::string_view nodeId() const { return _nodeId; };
const Message& answer() const { return _answer; };
const bool isAnswered() const { return _isAnswered; };
void setAnswer(const Message& answer) { _answer = answer; _isAnswered = true; std::cout << "release..." << std::endl; release(); };
void wait() { if (!_isAnswered) { _mutex.lock(); _mutex.lock(); _mutex.unlock(); } };
};
我有一个名为Manager
的类,它包含一个std::unordered_map<std::string, std::unique_ptr<Request>>
(键是Request::id()
),以及一个函数(称为makeRequest
),该函数在map中创建一个请求并返回对它的引用。
在makeRequest
返回之前,它将调用另一个函数sendToNode
,该函数将异步发送请求(请求字符串不存储在Request类中,只有应答),然后它返回对指针的引用,调用makeRequest
的函数现在对指针调用wait()。
之后,WebSocket将从节点异步读取,在Manager
中调用handleMessage
并解析请求应答的ID,从那里我们能够检查ID是否存在于unordered_map中,并调用setAnswer
以解锁wait
上的双互斥锁
我觉得执行wait
函数并锁定互斥锁两次是解决我的问题的一种可怕的方法,有没有其他的方法可以让我看到它?
1条答案
按热度按时间ctzwtxfj1#
这正是std::condition_variable所针对的情况。注意,这也是注解中推荐的等待std::future的典型实现方式。
如果你使用的是C++20,你可以做得更好,把_isAnswered设为一个std::atomic并使用std::atomic::wait(),基于你如何使用它,_isAnswered可能应该是原子的。
另一种方法是完全避免类似于future的接口,而使用异步完成回调,这是异步I/O库通常处理问题的方式,理由很充分,这种方法编写起来稍微复杂一些,但如果这很重要的话,它往往可以更好地扩展。
还要注意,std::mutex可能是递归的,因此双锁可能不会阻塞,并且从另一个线程释放std::mutex是未定义的,可能什么也不做,使程序陷入死锁。使用二进制信号量是一个潜在的替代方案,因为您想要的语义已经为该原语定义好了。