【问题标题】:How can I avoid lock of many threads on mutex which locked (owned) by thread which sleep?如何避免在互斥锁上锁定许多线程,这些线程由休眠的线程锁定(拥有)?
【发布时间】:2016-07-01 19:19:33
【问题描述】:

这个问题被称为Convoying:如果一个持有锁的线程由于时间片中断或页面错误而被取消调度,所有其他线程都必须等待。 https://en.wikipedia.org/wiki/Lock_(computer_science)#Disadvantages

众所周知,如果thread-1锁定了std::mutex并发生thread-1的切换,并且此时有很多线程(2、3、4 ...)想要锁定这个互斥体,那么所有这些线程都会已锁定,将等待开启线程 1。

解决方案是使用无锁算法。但是如果需要使用互斥锁,那是避免这种情况的一些解决方案吗?

  1. 如何提前100个周期才知道 线程切换?

  2. 或者我怎样才能提前100个周期之前引发异常 在 Linux x86_64 上切换流程?

  3. 或者我怎样才能让线程继续工作一段时间(100 周期)?

更新:

我有 20 个 CPU 核心,我的程序有 40 个线程,分为 2 部分:

  • 第 1 部分 - 20 个线程使用受 std::mutex mtx1 保护的第一个共享资源
  • 第 2 部分 - 20 个线程使用受 std::mutex mtx2 保护的第二个共享资源

众所周知,操作系统会为每个线程分配一定的工作时间,然后让他停止工作,并为下一个线程的空闲核心分配将运行相同时隙的内核。

第 1 部分: 有时,不经常,但这种情况对我来说很关键,发生 20 个线程中有 1 个执行 mtx1.lock() 然后开始使用共享资源,然后关闭操作系统(使该线程进入睡眠状态)mtx1.unlock() - 因为操作系统分配给该线程的时间已过期,操作系统决定让该线程进入睡眠状态。并且操作系统仅在 ~1 - 10 毫秒(30 000 000 个周期)后才打开该线程。在此期间,第 1 部分的 19 个其他线程至少一次尝试在 10 微秒(30 000 个周期)中的每个线程中获取共享资源,但mtx1 很忙。

然后,Part-1 的 19 个线程中的每一个都开始进入休眠状态,并且空闲的 CPU 内核被 Part-2 的线程占用。操作系统看到所有内核都处于忙碌状态,并且不会唤醒 Part-1 的线程。

这种情况并不经常发生,但是当这种情况发生时,第 1 部分(20 个线程)会冻结整个 1-10 毫秒(30 000 000 个周期),这对于任务来说是非常不可接受的。

第 1 部分的延迟超过 10 微秒(30 000 个周期)怎么会出现这种情况?

【问题讨论】:

  • 如果线程正在等待锁,那么操作系统应该切换到下一个线程,并继续循环直到到达解锁互斥锁的线程。
  • 我不确定我是否理解问题所在,因为在锁定线程运行时让所有其他线程阻塞正是使用互斥锁的关键。
  • “100 个周期”的相关性是什么——您能详细解释一下您的想法吗?
  • @Soren 如果我确定线程将在 100-1000 个周期后休眠,那么我确定我确定没有时间锁定互斥锁(标志的 CAS),完成工作并解锁互斥锁。在这个线程中yield()立即休眠更有利可图。
  • @Galik 问题是我在 20 个内核上有 40 个线程,1 个线程锁定互斥锁并休眠,因为操作系统需要这个,还有 19 个线程等待这个互斥锁并休眠,20 个线程不使用这个互斥体占据了所有 20 个内核。

标签: c++ multithreading c++11 concurrency posix


【解决方案1】:

无锁或接近无锁设计的要点在于,如果您确实需要某个互斥体,那么这种情况将是罕见的,因此任何两个线程在同一时间。

您对您的设计的解释和您准备采取的对策听起来像是您认为所有线程很可能都会命中互斥锁 - 所以您的想法是错误的,或者您的设计是错误的。

您无法读取调度程序的想法,但正如 here 所讨论的那样,您可以做一些事情可能会影响您的 threadscheduled 的方式 - 但是我建议不要玩周围有类似的东西。

【讨论】:

    猜你喜欢
    • 2014-10-19
    • 2021-02-19
    • 2013-01-31
    • 2012-12-25
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    • 2011-07-24
    相关资源
    最近更新 更多