Согласованность кэша и порождение потока - PullRequest
1 голос
/ 06 июня 2010

Фон

Я читал различные книги и статьи, чтобы узнать о процессорных кэшах, непротиворечивости кэш-памяти и барьерах памяти в контексте одновременного выполнения. Однако до сих пор я не смог определить, насколько безопасна моя обычная практика кодирования в самом строгом смысле.

Предположения

  1. Следующий псевдокод выполняется на двухпроцессорной машине:

    int sharedVar = 0;
    
    myThread()
    {
        print(sharedVar);
    }
    
    main()
    {
        sharedVar = 1;
        spawnThread(myThread);
        sleep(-1);
    }
    
  2. main () выполняется на процессоре 1 (P1), а myThread () выполняется на P2.

  3. Первоначально sharedVar существует в кешах P1 и P2 с начальным значением 0 (из-за некоторого «кода прогрева», который не показан выше.)

Вопрос

Строго говоря - желательно без учета какого-либо конкретного типа процессора - myThread () гарантированно печатает 1?

С моим новым знанием о процессорных кэшах, вполне возможно, что во время оператора print () P2, возможно, не получил запрос на аннулирование sharedVar, вызванный назначением P1 в main (). Поэтому представляется возможным, что myThread () может вывести 0.

Ссылки

Это статьи и книги, которые я читал:

  1. Модели согласованности совместно используемой памяти: учебное пособие
  2. Барьеры памяти: аппаратное представление для хакеров программного обеспечения
  3. Барьеры памяти ядра Linux
  4. Компьютерная архитектура: количественный подход

Ответы [ 2 ]

1 голос
/ 09 июня 2010

Строго говоря - предпочтительно без учета какого-либо конкретного типа процессора - myThread () гарантированно печатает 1?

Теоретически, он может печатать либо 0, либо 1, даже на x86, поскольку хранилища могут перемещаться после загрузок практически на любой архитектуре .

На практике было бы трудно сделать myThread() print 0.
Порождение потока, скорее всего, будет функционировать как неявное хранилище / освобождение барьер памяти , поскольку, вероятно, оно будет:
- иметь хотя бы одну инструкцию вдоль пути выполнения, которая приводит к барьеру памяти - заблокированные инструкции, явные инструкции барьера памяти и т. д.,
- или хранилище будет просто удалено / выгружено из буфера store к моменту вызова myThread(), поскольку установка нового потока приводит к выполнению многих инструкций - среди них много хранилищ.

0 голосов
/ 06 июня 2010

Я буду говорить только на Java здесь: myThread() гарантированно напечатает 1, потому что происходит до определения из Спецификация языка Java (раздел 17.4.5).

Запись в sharedVar в main() происходит до того, как порождает поток с функцией myThread(), потому что присвоение переменной идет первым в программном порядке . Затем, порождение потока происходит до любых действий в потоке, который запускается. Под транзитивностью определения в разделе 17.4.5 ( hb (x, y) и hb (y, z) подразумевается hb (x, z) ), запись в переменную sharedVar происходит раньше, чем print() читает sharedVar в myThread().

Вам также может понравиться чтение статьи Брайана Гетца Теория и практика Java: исправление модели памяти Java, часть 2 , охватывающей эту тему, а также его книга Параллелизм Java на практике .

...