A3. Producer–consumer
Обмежений буфер є моделлю будь-якої черги задач: пулу потоків, черги з’єднань, конвеєра обробки. Ви реалізуєте його двічі й самі побачите, за яких умов версія без блокувань виграє, а за яких програє.
Друга частина тут важливіша за першу. Твердження «lock-free швидше» поширене й без уточнення умов неправильне, а ви отримаєте власні числа.
Що має вийти
Section titled “Що має вийти”Дві програми з однаковим інтерфейсом:
./pc-mutex --producers 4 --consumers 4 --items 1000000 --capacity 1024./pc-lockfree --producers 4 --consumers 4 --items 1000000 --capacity 1024--items — загальна кількість елементів, поділена порівну між
виробниками. Елементи нумеруються від 1 до items, тому очікувана
контрольна сума завжди items × (items + 1) / 2. Споживачі підсумовують
те, що отримали.
Останній рядок виводу зафіксований, на нього спирається автоперевірка:
checksum=500000500000 expected=500000500000 elapsed_ms=412Сума мусить збігатися завжди. Незбіг означає, що елементи губляться або дублюються, і жоден вимір швидкості після цього не має сенсу.
-
Версія з м’ютексом і умовними змінними.
Буфер фіксованої місткості, один м’ютекс, дві умовні змінні: «є місце» і «є елемент». Виробник чекає на першій, споживач на другій.
Умову перевіряйте у циклі
while, не вif(модуль 9). -
Перевірка санітайзером.
Terminal window gcc -O2 -pthread -fsanitize=thread pc-mutex.c -o pc-tsan && ./pc-tsanThreadSanitizer має мовчати. Якщо він знаходить гонитву, а програма при цьому дає правильну відповідь — це саме той випадок, коли «працює» нічого не доводить.
-
Коректне завершення.
Споживачі мають вийти, коли виробники закінчили і буфер порожній. Найчастіша помилка курсу — споживач назавжди зависає в
wait, бо його вже нікому будити. -
Версія без блокувань.
Кільцевий буфер на атомарних індексах читання й запису. Публікація елемента —
compare_exchangeна індексі, потім запис значення, потім атомарне оновлення позначки готовності.Порядок операцій тут не косметика: без правильних
memory_orderспоживач побачить індекс раніше за дані (модуль 9). -
Замір.
Прогоніть обидві версії на 1, 2, 4 і 8 потоках з кожного боку, з малим і великим буфером. Побудуйте таблицю.
Поясніть письмово, чому на одному виробнику й одному споживачі різниця мала, а на восьми — велика; і чому за дуже малого буфера версія без блокувань може програти.
Автоперевірка
Section titled “Автоперевірка”Перевірка лежить в архіві з файлами курсу, і команди нижче виконуються з розпакованого каталогу.
cd labs/a3-producer-consumer./check.sh ./pc-mutex ./pc-lockfreeПеревірка ганяє обидві версії на різних співвідношеннях потоків, звіряє контрольні суми й ловить зависання за таймаутом.
Часті помилки
Section titled “Часті помилки”Втрачені або задвоєні елементи. Контрольна сума не збігається. Майже завжди причина в тому, що індекс оновлено не атомарно або що перевірка місця й запис розділені.
Програма зависає наприкінці. Або немає сигналу завершення для споживачів,
або broadcast замінили на signal там, де чекає кілька потоків.
if замість while навколо wait. Працює рівно доти, доки споживач
один.
Заміри без прогріву. Перший прогін включає створення потоків і холодні кеші, тож робіть кілька повторів і беріть медіану.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Порівняти з чергою на двох м’ютексах (окремо для голови й хвоста)
і з pthread_spinlock — і побачити, що середина між двома крайнощами
часто виграє в обох.