【问题标题】:Multithreading on multiple core/processors多核/处理器上的多线程
【发布时间】:2020-02-26 06:25:48
【问题描述】:

我的想法是,如果锁定和解锁互斥锁是一项原子操作,那么它可以在单处理器架构的情况下保护代码的关键部分。 任何首先被调度的线程都能够在单个机器代码操作中“锁定”互斥锁。 但是当线程在多个内核上运行时,互斥锁有什么用呢? (不同的线程可以同时在不同的“核心”上同时运行)。 我似乎无法理解多线程程序如何在多个内核上没有任何死锁或竞争条件的情况下工作?

【问题讨论】:

  • “我似乎无法理解多线程程序如何在没有任何死锁或多核竞争条件的情况下工作?” -- 为什么不呢?您的问题非常不清楚,并且线程信号对象(如互斥体)背后的机制涉及相当多,因此对它们的任何详细解释对于堆栈溢出问题来说都过于宽泛。

标签: multithreading mutex


【解决方案1】:

线程由操作系统管理,其中包括负责将线程调度到内核,因此它还可以避免将特定线程调度到内核上。

互斥锁是一个操作系统概念。您基本上是在要求操作系统阻止一个线程,直到其他线程告诉操作系统它没问题

【讨论】:

    【解决方案2】:

    一般答案:

    互斥锁是一个操作系统概念。提供互斥锁的操作系统必须确保这些互斥锁在该操作系统想要支持的所有硬件上正常工作。如果无法为特定硬件实现互斥锁,则操作系统无法在该硬件上提供互斥锁。如果操作系统需要存在互斥体才能正常工作,那么它根本无法支持该硬件。毫无疑问,操作系统如何为特定硬件实现互斥锁非常依赖于硬件,并且在操作系统及其支持的硬件之间存在很大差异。


    详细答案:

    大多数通用 CPU 都提供原子操作。这些操作旨在跨系统中的所有 CPU 内核进行原子操作,无论这些内核是单个 CPU 还是多个单独 CPU 的一部分。

    只需两个原子操作atomic_oratomic_and,就可以实现锁。例如。想想

    int atomic_or ( int * addr, int val )

    它自动计算*addr = *addr | val 并在执行计算之前返回*addr 的旧值。如果*lock == 0 和多个线程调用atomic_or(lock, 1),那么其中只有一个会得到0 作为结果;只有第一个线程执行该操作。结果所有其他线程都得到1。获得0 的线程获胜,它拥有锁,所有其他线程注册一个事件并进入睡眠状态。

    获胜者线程现在可以独占访问atomic_or 之后的部分,它可以执行所需的工作,一旦完成,它就会再次清除锁(atomic_and(lock, 0))并生成一个系统事件,即锁现在再次可用。

    然后系统将在进入睡眠之前唤醒一个、部分或所有为此事件注册的线程,并重新开始争夺锁。任何一个被唤醒的线程都将赢得比赛,也可能没有,因为另一个线程更快,并且可能在atomic_and之间和其他线程甚至被唤醒之前抓住了锁,但这没关系,仍然正确,因为它仍然只有一个线程可以访问。所有未能获得锁的线程都重新进入休眠状态。

    当然,现代系统的实际实现往往比这复杂得多,它们可能会考虑线程优先级(在锁竞争中可能首选高优先级线程),或者可能确保每个线程都在等待互斥锁最终也会得到它(存在防止线程总是失去锁竞争的预防措施)。互斥体也可以是递归的,在这种情况下,系统会确保同一个线程可以多次获得同一个互斥体而不会死锁,这需要一些额外的记录。

    可能不用说,但原子操作是更昂贵的操作,因为它们需要系统内的内核同步它们的工作,这会降低它们的处理吞吐量。如果所有内核都在单个 CPU 上运行,它们可能会有些昂贵,但如果有多个 CPU,它们甚至可能非常昂贵,因为同步必须通过 CPU 总线系统进行,该总线系统将 CPU 相互连接,而这个总线系统通常不会以 CPU 速度级别运行。

    另一方面,使用互斥体总是会减慢处理速度,因为如果多个线程需要同时访问以继续工作,则提供对资源的独占访问必须减慢处理速度。因此,对于实现互斥锁,这是无关紧要的。实际上,如果您可以仅使用原子操作而不是全功能互斥锁以线程安全的方式实现函数,那么您通常会获得明显的速度优势,尽管这些操作比普通操作更昂贵。

    【讨论】:

      【解决方案3】:

      在现代操作系统上,线程是对物理硬件的抽象。程序员将线程作为代码执行的抽象。在可用的硬件内核上工作没有单独的抽象。操作系统负责将线程映射到物理内核。

      互斥锁是一种存在于系统内存中的数据结构。任何有权访问的线程都可以读取该内存位置,而不管它在哪个线程或内核中运行。无论您的代码是在内核 1 还是内核 20 上执行,它仍然能够读取当前状态锁。

      换句话说,无论线程或内核的数量如何,它们都只有共享的系统内存可供它们操作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-22
        相关资源
        最近更新 更多