Page 72 - 4989
P. 72
імовірно завершиться помилкою. У табл. 7.1 показано ситуацію, в
якій два потоки одночасно виконують фрагмент коду на зразок
N+=delta;
де N – це глобальна змінна. Рядки таблиці представляють
інтервали часу, що слідують один за одним.
Таблиця 7.1 – Приклад конфлікту доступу до глобальної
змінної
Потік 1 Потік 2
зчитування значення N в зчитування значення N в
регістр процесора 1 регістр процесора 2
зміна значення в регістрі зміна значення в регістрі
процесора 1 (додавання delta) процесора 2 (додавання delta)
запис значення в оперативну очікування доступу до пам’яті
пам’ять
запис значення в оперативну
пам’ять
Одночасний запис в оперативну пам’ять двох різних значень
фізично неможливий, тому менеджер пам’яті призупинить
виконання одного із потоків, поки другий потік не виконає запис,
і відновить виконання після завершення запису. Однак
результуюче значення змінної N після виконання цього коду
збільшиться на delta, а не на delta*2 (як, очевидно, було
задумано), бо другий потік буде оновлювати старе значення N,
отримане з пам’яті одночасно з першим потоком. Слід зауважити,
що подібна ситуація може статися навіть тоді, коли в системі
присутній всього один логічний процесор, якщо потік 2 буде
витіснений потоком 1 безпосередньо перед записом значення в
оперативну пам’ять.
Для уникнення такої ситуації необхідно, щоб виконання
рядка N+=delta для деякого потоку було відкладено, поки його
виконує інший потік. Найпростіший спосіб такого блокування
забезпечує об’єкт синхронізації «критична секція»
(CRITICAL_SECTION). Критичну секцію необхідно
ініціалізувати функцією InitializeCriticalSection, після чого
деякий фрагмент коду можна захистити за допомогою функцій
EnterCriticalSection та LeaveCriticalSection:
72