Page 82 - 4868
P. 82

Ошибка! Стиль не определен.                                                                80

               будь-який з процесів знову спробує її збільшити.
                     Дану проблему можна вирішити за допомогою двох лічильників, один з
               яких  збільшується  до  n,  а  інший  зменшується  до  0.  Їхні  ролі  міняються
               місцями  після  кожного  етапу.  Проте,  використання  спільних  лічильників
               породжує  суто  технічну  проблему  їх  реалізації.  По-перше,  збільшувати  і
               зменшувати  їх  значення  потрібно  неподільним  чином.  По-друге,  коли  в
               програмі  процес  припиняє  своє  виконання,  то  він  безперервно  перевіряє
               значення  змінної  count.  У  гіршому  випадку  n-1  процесів  очікуватимуть,

               поки  останній  процес  не  досягне  бар’єру.  У  результаті  виникне  серйозний
               конфлікт  звернення  до  пам’яті,  якщо  тільки  програма  не  виконується  на
               мультипроцесорній машині з узгодженою кеш-пам’яттю.
                     Таким чином, реалізація бар’єру з використанням лічильників можлива,
               тільки  якщо  на  цільовий  машині  є  неподільні  інструкції  інкременту  і
               узгоджена  кеш-пам’ять  з  ефективним  механізмом  оновлення.  Крім  того,
               число n повинно бути відносно малим.

                     12.2. Прапорці та управляючі процеси

                     Одним  із  способів  уникнення  конфліктів  звернення  до  пам’яті  є
               реалізація  лічильника  count  за  допомогою  n  змінних,  значення  яких
               сумуються  між  собою.  Нехай,  наприклад,  існує  масив  цілих  чисел
               arrive[1: n]  з  нульовими  початковими  значеннями.  Замінимо  операцію
               інкременту       лічильника       count      в    програмі      операцією       присвоєння
               arrive[i] = 1. Тоді глобальним інваріантом буде наступний предикат.

                     count == (arrive[l] + ... + arrive[n])

                     Конфліктів  звернення  до  пам’яті  можна  уникнути,  якщо  елементи
               масиву arrive зберігаються в різних рядках кеш-пам’яті.
                     У  програмі  залишилося  реалізувати  оператор  await  і  обнулити
               елементи масиву arrive в кінці кожної ітерації. Одна із реалізацій оператора
               await може мати наступний вигляд:

                     <await ((arrive[1] + ... + arrive[n]) == n);>
                     Але в такому випадку знову виникають конфлікти звернення до пам’яті,
               причому  таке  рішення  є  досить  неефективним,  оскільки  суму  елементів
               arrive[i]  тепер  постійно  обчислює  кожен  очікуючий  своєї  черги  процес

               Worker.
                     Обидві проблеми (конфлікти звернення до пам’яті і обнулення масиву)
               можна  вирішити,  використавши  додатковий  набір  спільних  значень  і
               додатковий процес Coordinator. Нехай кожен процес Worker замість того,
               щоб  підсумувати  елементи  масиву  arrive,  очікує,  доки  єдине  логічне
               значення  не  стане  «істина».  Нехай  continue[1: n]  –  додатковий  масив
               цілих  чисел  з  нульовими  початковими  значеннями.  Після  того  як  процес
               Worker[i]  присвоїть  значення  1  елементу  масиву  arrive[i],  він
               переводиться  в  режим  очікування  до  того  часу,  доки  значенням  змінної
               continue[i] не стане 1.
   77   78   79   80   81   82   83   84   85   86   87