Есть ли проблема с синхронизацией в этой программе? - PullRequest
0 голосов
/ 28 марта 2020

Рассмотрим следующую программу.
(i) Какими будут выходные данные в строке A и строке B? Обоснуйте свой ответ.
(ii) Как вы думаете, существует ли проблема с синхронизацией при обновлении значения переменной?
Обоснуйте свой ответ.

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>

int value = 100;
void *thread_prog(void *param);

int main(int argc, char *argv[])
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_prog, NULL);
    pthread_join(tid, NULL);
    value = value + 100;
    printf("Parent value = %d\n", value); //Line A
}
void *thread_prog(void *param)
{
    value = value + 100;
    printf("Child value = %d\n", value); // Line B
    pthread_exit(0);
}

Выходными данными будут строка A, равная 300, и строка B, равная 200. Я не думаю, что существует проблема синхронизации из-за pthread_join(tid, NULL);

Ответы [ 2 ]

1 голос
/ 28 марта 2020

В размещенном коде поток управления очевиден, поэтому нет проблем с отношениями до и после заказа. Но для правильной синхронизации многопоточного кода требуется нечто большее, чем просто установление правильных связей «до и после».

Необходимо учитывать два дополнительных аспекта, чтобы гарантировать отсутствие проблем с синхронизацией в этом коде.

  • Видны ли изменения, сделанные в дочернем потоке, в основном потоке?
  • Do семантика C абстрактной машины не позволяет компилятору C предполагать, что содержимое переменная value не изменяется во время работы основного потока?

Этот ответ касается только первой проблемы.

Недостаточно просто установить sh гарантированное отношение «до и после» в многопоточном коде, чтобы гарантировать, что изменение переменной полностью просматривается другим потоком. Запись в Википедии о «барьере памяти» дает хорошее объяснение :

Барьер памяти, также известный как инструкция с перепонкой, забором памяти или забором, является типом инструкции барьера это заставляет центральный процессор (ЦП) или компилятор принудительно устанавливать ограничение порядка операций с памятью, выполненное до и после инструкции барьера. Как правило, это означает, что операции, выполненные до барьера, гарантированно будут выполнены до операций, выполненных после барьера.

Барьеры памяти необходимы, потому что большинство современных процессоров используют оптимизацию производительности, которая может привести к неупорядоченному выполнению. Такое переупорядочение операций с памятью (загрузка и сохранение) обычно проходит незамеченным в пределах одного потока выполнения, но может привести к непредсказуемому поведению в параллельных программах и драйверах устройств, если их тщательно не контролировать. Точная природа ограничения упорядочения зависит от аппаратного обеспечения и определяется моделью упорядочения памяти в архитектуре. В некоторых архитектурах предусмотрено несколько барьеров для применения различных ограничений порядка.

Другими словами, изменение, внесенное в переменную, работающую, например, на ЦП 1, может не «просматриваться» другим работающим потоком, скажем, CPU 7, даже несмотря на то, что код запускается на CPU 7 после того, как код, который изменил переменную, запускается на CPU 1.

Должна быть какая-то реализация, зависящая от платформы c, гарантирующая, что такие изменения распространяются через фактическое оборудование и видимы.

И модель потоков POSIX определяет эти точные гарантии.

Per POSIX 4.12 Синхронизация памяти (шахтное выделение ):

Приложения должны гарантировать, что доступ к любой ячейке памяти более чем одним потоком управления (потоками или процессами) ограничен, так что ни один поток управления не может читать или изменять местоположение памяти, пока другой поток контроля может изменить его. Такой доступ ограничен с помощью функций, которые синхронизируют выполнение потока, а также синхронизируют память относительно других потоков. Следующие функции синхронизируют память с другими потоками :

 .
 .
 .
pthread_create()
pthread_join()
 .
 .
 .

Использование pthread_create() и pthread_join() не только устанавливает порядок упорядочения до и после, необходимый для правильная синхронизация, но в соответствии со стандартом POSIX они также устанавливают sh требования к видимости.

Так что да, опубликованный код должным образом синхронизируется в гарантированном порядке до и после заказа, а также с аспектами видимости.

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

0 голосов
/ 28 марта 2020

Вы правильно предполагаете, что проблемы синхронизации нет, pthread_join () приостанавливает вызывающий поток до тех пор, пока целевой поток не завершится. Если целевой поток уже завершился, он немедленно возвращается. В любом случае целевой регистр выполняется и завершается до вывода строки A.

...