代码之家  ›  专栏  ›  技术社区  ›  csj

线程系统如何处理由不同CPU缓存的共享数据?

  •  9
  • csj  · 技术社区  · 15 年前

    我主要来自C++背景,但我认为这个问题适用于任何语言的线程。下面是场景:

    1. 我们有两个线程(threada和threadb),共享内存中有一个值x。

    2. 假设对x的访问由互斥体(或其他合适的同步控制)适当地控制。

    3. 如果线程恰好在不同的处理器上运行,那么如果线程执行写操作,但其处理器将结果放在二级缓存而不是主内存中,会发生什么情况?然后,如果threadb尝试读取该值,它会不会不仅仅在自己的l1/l2缓存/主内存中查找,然后使用存在的旧值?

    如果不是这样,那么这个问题是如何管理的?

    如果是这样的话,那该怎么办呢?

    4 回复  |  直到 15 年前
        1
  •  10
  •   Michael    15 年前

    你的例子很好用。

    多处理器使用 coherency protocol MESI 以确保数据在缓存之间保持同步。对于MESI,每个缓存线都被认为是被修改、独占、在CPU之间共享的,或者是无效的。写一条在处理器之间共享的缓存线会强制它在另一个CPU中无效,从而保持缓存的同步。

    然而,这还不够。不同的处理器有不同的 memory models 和大多数现代处理器支持某种程度的重新排序内存访问。在这些情况下, memory barriers 是需要的。

    例如,如果您有线程A:

    DoWork();
    workDone = true;
    

    线程B:

    while (!workDone) {}
    DoSomethingWithResults()
    

    由于这两个处理器都在不同的处理器上运行,因此无法保证在线程B看到DoWork()内完成的写入操作之后,对WorkDone和DoSomethingWithResults()的写入操作将以潜在的不一致状态继续进行。内存屏障保证了读和写的某些顺序——在线程A中的dowork()之后添加一个内存屏障将强制dowork完成的所有读/写操作在write to workdone之前完成,这样线程B将获得一致的视图。互斥锁本质上提供了一个内存屏障,这样读/写就不能通过调用来锁定和解锁。

    在您的情况下,一个处理器会向其他处理器发出信号,指出它弄脏了缓存线,并强制其他处理器从内存重新加载。获取互斥量以读取和写入该值,可以保证对内存的更改对其他处理器是可见的,并且是按预期的顺序进行的。

        2
  •  1
  •   ephemient    15 年前

    大多数锁原语如互斥体意味着 memory barriers . 这些强制执行缓存刷新和重新加载。

    例如,

    ThreadA {
        x = 5;         // probably writes to cache
        unlock mutex;  // forcibly writes local CPU cache to global memory
    }
    ThreadB {
        lock mutex;    // discards data in local cache
        y = x;         // x must read from global memory
    }
    
        3
  •  0
  •   Paul Sonier    15 年前

    一般来说,编译器理解共享内存,并花费大量精力确保将共享内存放置在可共享的位置。现代编译器在排序操作和内存访问方面非常复杂;它们倾向于理解线程和共享内存的本质。这并不是说它们是完美的,但一般来说,编译器会处理大部分问题。

        4
  •  0
  •   Dykam    15 年前

    C对此类问题有一些内置支持。 您可以用 volatile 关键字,强制它在所有CPU上同步。

    public static volatile int loggedUsers;
    

    另一部分是围绕名为threading.monitor.enter(x)和threading.monitor.exit(x)的.NET方法的语法包装器,其中x是要锁定的变量。这会导致试图锁定X的其他线程必须等待,直到锁定线程调用退出(X)。

    public list users;
    // In some function:
    System.Threading.Monitor.Enter(users);
    try {
       // do something with users
    }
    finally {
       System.Threading.Monitor.Exit(users);
    }
    
    推荐文章