【问题标题】:How to use mutex properly?如何正确使用互斥锁?
【发布时间】:2021-03-30 01:30:40
【问题描述】:

我有邮箱类,线程之间共享发送和接收方法,线程:线程 1 发送消息,线程 2 和 3 接收消息,我必须如何使用互斥锁进行同步?

我尝试过的任何组合都没有成功。

std::mutex g_lock; //in global
void sendMessage(Message msg) {
    if (g_lock.try_lock()) {
        this_thread::sleep_for(100ms); // DELAY
        messages->push_back(msg);
        g_lock.unlock();
    }
}

Receive方法也一样

完整代码: https://pastebin.com/7y2RC5br

此外,这段代码也无法调试,因为延迟会改变代码的逻辑。

代码的正确逻辑: thread2/3 尝试锁定并读取消息,清空然后解锁 thread1 尝试锁定并发送消息然后解锁 thread2/3 尝试锁定并读取 msg,获取 msg 并写入文件然后解锁

当我从线程 2/3 尝试互斥锁的 try_lock 时,我一直在不断地阻塞线程并且线程 1 在所有线程 2/3 之后一直在工作。

【问题讨论】:

  • 你应该使用std::lock_guard.`
  • try_lock,如果没有获得锁,你将不会发送任何消息。
  • 但是为什么不能获取锁呢?
  • 你应该决定你是否负担得起不锁定。在某些情况下,您需要使用lock() 而不是try_lock()
  • 回复。 "But why lock can't be acquired?":大概是因为它已经被另一个线程拥有了?您确实需要提供一个 minimal reproducible example 来证明问题。

标签: c++ std mutex


【解决方案1】:

将低级互斥锁组合成构建块类型。

基于锁的线程安全非常容易出错。它不仅脆弱,而且不合成;三个成对线程安全的例程在组合时可能是不安全的。

线程安全是一种关系属性,而不是绝对属性。

有多种已知模式已在数学上证明有效。正确实施,你就不会那么失败了。折腾一些东西,你的代码就不会正确。

要设计新的线程代码,您需要精通基于证明的计算机科学。实现比正确设计更容易。

在您的情况下,我将从线程安全队列原语开始。您将需要条件变量、互斥体和标准双端队列。然后将每个例程连接到队列的一端,并将消息发送到另一端。 (读取器应该只从队列中读取并按顺序消费每条消息;写入器只能在队列上发送消息)。

我的意思是,这仍然很难解决,但至少你的原语不是几乎原始的互斥锁。

template<class T>
struct threadsafe_queue {
  T pop();
  void push(T);
  std::deque<T> pop_all();

  std::optional<T> try_pop();
  template<class...Ts>
  std::optional<T> wait_for_pop( std::chrono::duration<Ts...> );
private:
  mutable std::mutex m;
  std::condition_variable cv;
  std::deque<T> queue;
  std::unique_lock<std::mutex> lock() const;
};

【讨论】:

  • 我开始使用带有 condition_variable 的 unique_lock,它比只使用一个互斥锁要好得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
相关资源
最近更新 更多