c++ 为异步操作锁定互斥锁两次感觉不对

fumotvh3  于 2023-02-26  发布在  其他
关注(0)|答案(1)|浏览(116)

我正在使用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函数并锁定互斥锁两次是解决我的问题的一种可怕的方法,有没有其他的方法可以让我看到它?

ctzwtxfj

ctzwtxfj1#

这正是std::condition_variable所针对的情况。注意,这也是注解中推荐的等待std::future的典型实现方式。
如果你使用的是C++20,你可以做得更好,把_isAnswered设为一个std::atomic并使用std::atomic::wait(),基于你如何使用它,_isAnswered可能应该是原子的。
另一种方法是完全避免类似于future的接口,而使用异步完成回调,这是异步I/O库通常处理问题的方式,理由很充分,这种方法编写起来稍微复杂一些,但如果这很重要的话,它往往可以更好地扩展。
还要注意,std::mutex可能是递归的,因此双锁可能不会阻塞,并且从另一个线程释放std::mutex是未定义的,可能什么也不做,使程序陷入死锁。使用二进制信号量是一个潜在的替代方案,因为您想要的语义已经为该原语定义好了。

相关问题