В следующем упрощённом C‑коде с потоками pthreads возникает гонка: int counter=0; void*inc(void*arg){ for(int i=0;i

20 Ноя в 08:42
3 +2
0
Ответы
1
Проблема: одновременно несколько потоков выполняют неблокируемое чтение/запись общей переменной `counter` — операция `counter++` неатомарна, возникает гонка за данными и неверный итог.
Корректные способы синхронизации (с примерами):
1) Мьютекс (правильно, но сильно сериализует доступ)
```
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
void* inc(void* arg){
for(int i = 0; i < 100000100000100000; ++i){
pthread_mutex_lock(&m);
counter++;
pthread_mutex_unlock(&m);
}
return NULL;
}
```
2) C11 атомики (лучше по производительности при высокой конкуренции)
```
#include atomic_int counter = ATOMIC_VAR_INIT(0);
void* inc(void* arg){
for(int i = 0; i < 100000100000100000; ++i)
atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);
return NULL;
}
```
3) Батчинг (рекомендация для максимальной производительности — уменьшает число синхронных операций)
```
atomic_int counter = ATOMIC_VAR_INIT(0);
void* inc(void* arg){
int local = 0;
const int C = 100100100; // размер батча
for(int i = 0; i < 100000100000100000; ++i){
local++;
if(local == C){
atomic_fetch_add_explicit(&counter, local, memory_order_relaxed);
local = 0;
}
}
if(local) atomic_fetch_add_explicit(&counter, local, memory_order_relaxed);
return NULL;
}
```
Влияние на производительность (кратко):
- Без синхронизации — некорректно, но максимально быстро (ноль затрат на синхронизацию).
- С мьютексом — каждое ++ выполняется под блокировкой: доступ почти полностью сериализуется, высокая задержка/низкая пропускная способность при большом числе потоков.
- С атомиками — быстрее, так как операции атомарного инкремента менее дорогие, но при интенсивном обновлении одного счётчика возникает "ping‑pong" кеш‑линий между ядрами, что ограничивает масштабируемость.
- Батчинг снижает количество дорогостоящих синхронных операций примерно в CCC раз: число атомик‑операций ≈ T×⌈100000/C⌉T \times \lceil 100000 / C \rceilT×100000/C для TTT потоков, что обычно даёт существенный прирост.
Ожидаемый корректный итог: при TTT потоках final = T×100000T \times 100000T×100000.
Рекомендация: для простого счётчика используйте атомики с memory_order_relaxed; если потоков много и частые обновления — примените локальные буферы/батчинг и редкие атомарные добавления.
20 Ноя в 09:37
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир