Это не важно Вы все еще можете ужасно потерпеть неудачу десятками способов. Например, что произойдет, если поток, назначенный заданию, увидит запись в флаг, но не саму запись? Поток будет выполнять неправильную работу полностью.

те представим, что у меня есть несколько рабочих потоков, таких как:

while (1) {
    do_something();

    if (flag_isset())
        do_something_else();
}

У нас есть пара вспомогательных функций для проверки и установки флага:

void flag_set()   { global_flag = 1; }
void flag_clear() { global_flag = 0; }
int  flag_isset() { return global_flag; }

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

Мой вопрос:Нужно ли делать что-то особенное для синхронизации доступа к global_flag? Если да, какова минимальная работа для переноса синхронизации?

Я попытался выяснить это, прочитав много статей, но я все еще не совсем уверен в правильном ответе ... Я думаю, что это одно из следующего:

A: Нет необходимости синхронизировать, потому что установка или очистка флага не создает условия гонки:

Нам просто нужно определить флаг какvolatile чтобы убедиться, что он действительно читается из общей памяти при каждой проверке:

volatile int global_flag;

Он может не распространяться сразу на другие ядра процессора, но рано или поздно гарантированно.

B: Полная синхронизация необходима, чтобы убедиться, что изменения флага распространяются между потоками:

Установка общего флага в одном ядре процессора не обязательно делает его видимым для другого ядра. Нам нужно использовать мьютекс, чтобы убедиться, что изменения флагов всегда распространяются путем аннулирования соответствующих строк кэша на других процессорах. Код становится следующим:

volatile int    global_flag;
pthread_mutex_t flag_mutex;

void flag_set()   { pthread_mutex_lock(flag_mutex); global_flag = 1; pthread_mutex_unlock(flag_mutex); }
void flag_clear() { pthread_mutex_lock(flag_mutex); global_flag = 0; pthread_mutex_unlock(flag_mutex); }

int  flag_isset()
{
    int rc;
    pthread_m,utex_lock(flag_mutex);
    rc = global_flag;
    pthread_mutex_unlock(flag_mutex);
    return rc;
}
C: Синхронизация необходима, чтобы убедиться, что изменения флага распространяются между потоками:

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

volatile int    global_flag;
pthread_mutex_t flag_mutex;

void flag_set()   { pthread_mutex_lock(flag_mutex); global_flag = 1; pthread_mutex_unlock(flag_mutex); }
void flag_clear() { pthread_mutex_lock(flag_mutex); global_flag = 0; pthread_mutex_unlock(flag_mutex); }

int  flag_isset() { return global_flag; }

Это позволило бы избежать постоянной блокировки и разблокировки мьютекса, когда мы знаем, что флаг редко изменяется. Мы просто используемпобочный эффект из Pthreads мьютексов, чтобы убедиться, что изменение распространяется.

Итак, какой?

Я думаю, что A и B - очевидный выбор, B - безопаснее. Но как насчет C?

Если C в порядке, есть ли другой способ заставить изменение флага быть видимым на всех процессорах?

Есть один несколько связанный вопрос:Гарантирует ли защита переменной с помощью мьютекса pthread, что она также не кэшируется? ... но это на самом деле не отвечает на это.

Ответы на вопрос(4)

Ваш ответ на вопрос