logo

АЛГОРИТМ ПЕТЕРСОН-а за међусобно искључење | Сет 2 (ЦПУ циклуси и меморијска ограда)

Проблем: С обзиром на 2 процеса И и Ј, морате да напишете програм који може гарантовати међусобно искључење између њих двоје без икакве додатне хардверске подршке.

Расипање ЦПУ Цлоцк Цицлес-а

У лаику појмовима када је нит чекала да се окрене, завршила је дуготрајна петља која је тестирала услов милионе пута у секунди тако непотребно израчунавање. Постоји бољи начин да се чека и то је познато као 'принос' .



Да бисмо разумели шта нам је потребно да дубоко копамо у начину рада процеса у Линуку. Овде је поменута идеја је поједностављена верзија планера Стварна имплементација има пуно компликација.

Размотрите следећи пример 
Постоје три процеса П1 П2 и П3. Процесс П3 је такав да има ио време Слично слично ономе у нашем кодексу који није тако корисно рачунање и постоји од петље само када П2 заврши његово извршење. Планинар их све ставља у округли робин ред. Сада реците да је брзина сата процесора 1000000 / сек и додељује 100 сатова у сваком поступку у свакој итерацији. Затим ће се први П1 покренути за 100 сатова (0,0001 секунди), а затим П2 (0,0001 секунди), пошто не постоји више процеса овог циклуса понавља се до П2, а затим је уследио П3-ова и на крају, а затим је уследила и на крају П3.

Ово је комплетан отпад циклуса 100 ЦПУ-а. Да бисте избегли да се међусобно одустанемо од временског времена ЦПУ-а, тј. Принос који се у суштини завршава овај пут, и распоред преузме следећи процес који је следећи процес покренуо. Сада тестирамо своје стање једном кад се одрекнемо ЦПУ-а. С обзиром на наш тест траје 25 циклуса сата уштедемо 75% нашег рачунања у времену. Да ово графички постави
 



АЛГОРИТМ ПЕТЕРСОН-а за међусобно искључење | Сет 2 (ЦПУ циклуси и меморијска ограда)

С обзиром на брзину сата процесора као 1МХз, ово је пуно штедње!. 
Различите дистрибуције пружају различиту функцију за постизање ове функције. Линук пружа сцхед_ииелд () .

C
void lock(int self) {  flag[self] = 1;  turn = 1-self;  while (flag[1-self] == 1 &&  turn == 1-self)    // Only change is the addition of  // sched_yield() call  sched_yield(); } 

Меморијска ограда.

Кодекс у ранијем водичу можда је радио на већини система, али није 100% тачно. Логика је била савршена, али најмодерније ЦПУ-у запошљава оптимизације перформанси које могу резултирати извршењем извршења ванредне наруџбе. Ово преуређивање меморијских операција (оптерећења и продавнице) обично препазиће у једној нити извршења, али може проузроковати непредвидиво понашање у истодобним програмима.
Размислите о овом примеру 



C
 while (f == 0);    // Memory fence required here  print x; 

У горњем примеру преводилац разматра 2 изјаве као независно једни од других и на тај начин покушава повећати ефикасност кодекса поновним наручивањем којих могу довести до проблема са истодобним програмима. Да бисте избегли ово место меморијску ограду да дате наговештајник компајлеру о могућем односу између изјава широм баријере.

Дакле, редослед изјава  

застава [селф] = 1; 
ред = 1-себство; 
док (претвори чек у услов) 
принос (); 
 

мора бити потпуно исти како би закључали рад у супротном, завршиће у стању застоја.

Да би се осигурало да се овај преводила пружају упутство које спречавају наручивање изјава широм ове баријере. У случају ГЦЦ-а то је __Синц_Синцхронизе () .
Дакле, модификовани код постаје 
Потпуна примена у Ц:

C++
// Filename: peterson_yieldlock_memoryfence.cpp // Use below command to compile: // g++ -pthread peterson_yieldlock_memoryfence.cpp -o peterson_yieldlock_memoryfence #include   #include #include   std::atomic<int> flag[2]; std::atomic<int> turn; const int MAX = 1e9; int ans = 0; void lock_init() {  // Initialize lock by resetting the desire of  // both the threads to acquire the locks.  // And giving turn to one of them.  flag[0] = flag[1] = 0;  turn = 0; } // Executed before entering critical section void lock(int self) {  // Set flag[self] = 1 saying you want  // to acquire lock  flag[self]=1;  // But first give the other thread the  // chance to acquire lock  turn = 1-self;  // Memory fence to prevent the reordering  // of instructions beyond this barrier.  std::atomic_thread_fence(std::memory_order_seq_cst);  // Wait until the other thread loses the  // desire to acquire lock or it is your  // turn to get the lock.  while (flag[1-self]==1 && turn==1-self)  // Yield to avoid wastage of resources.  std::this_thread::yield(); } // Executed after leaving critical section void unlock(int self) {  // You do not desire to acquire lock in future.  // This will allow the other thread to acquire  // the lock.  flag[self]=0; } // A Sample function run by two threads created // in main() void func(int s) {  int i = 0;  int self = s;  std::cout << 'Thread Entered: ' << self << std::endl;  lock(self);  // Critical section (Only one thread  // can enter here at a time)  for (i=0; i<MAX; i++)  ans++;  unlock(self); } // Driver code int main() {   // Initialize the lock   lock_init();  // Create two threads (both run func)  std::thread t1(func 0);  std::thread t2(func 1);  // Wait for the threads to end.  t1.join();  t2.join();  std::cout << 'Actual Count: ' << ans << ' | Expected Count: ' << MAX*2 << std::endl;  return 0; } 
C
// Filename: peterson_yieldlock_memoryfence.c // Use below command to compile: // gcc -pthread peterson_yieldlock_memoryfence.c -o peterson_yieldlock_memoryfence #include #include #include 'mythreads.h' int flag[2]; int turn; const int MAX = 1e9; int ans = 0; void lock_init() {  // Initialize lock by resetting the desire of  // both the threads to acquire the locks.  // And giving turn to one of them.  flag[0] = flag[1] = 0;  turn = 0; } // Executed before entering critical section void lock(int self) {  // Set flag[self] = 1 saying you want  // to acquire lock  flag[self]=1;  // But first give the other thread the  // chance to acquire lock  turn = 1-self;  // Memory fence to prevent the reordering  // of instructions beyond this barrier.  __sync_synchronize();  // Wait until the other thread loses the  // desire to acquire lock or it is your  // turn to get the lock.  while (flag[1-self]==1 && turn==1-self)  // Yield to avoid wastage of resources.  sched_yield(); } // Executed after leaving critical section void unlock(int self) {  // You do not desire to acquire lock in future.  // This will allow the other thread to acquire  // the lock.  flag[self]=0; } // A Sample function run by two threads created // in main() void* func(void *s) {  int i = 0;  int self = (int *)s;  printf('Thread Entered: %dn'self);  lock(self);  // Critical section (Only one thread  // can enter here at a time)  for (i=0; i<MAX; i++)  ans++;  unlock(self); } // Driver code int main() {   pthread_t p1 p2;  // Initialize the lock   lock_init();  // Create two threads (both run func)  Pthread_create(&p1 NULL func (void*)0);  Pthread_create(&p2 NULL func (void*)1);  // Wait for the threads to end.  Pthread_join(p1 NULL);  Pthread_join(p2 NULL);  printf('Actual Count: %d | Expected Count:'  ' %dn'ansMAX*2);  return 0; } 
Java
import java.util.concurrent.atomic.AtomicInteger; public class PetersonYieldLockMemoryFence {  static AtomicInteger[] flag = new AtomicInteger[2];  static AtomicInteger turn = new AtomicInteger();  static final int MAX = 1000000000;  static int ans = 0;  static void lockInit() {  flag[0] = new AtomicInteger();  flag[1] = new AtomicInteger();  flag[0].set(0);  flag[1].set(0);  turn.set(0);  }  static void lock(int self) {  flag[self].set(1);  turn.set(1 - self);  // Memory fence to prevent the reordering of instructions beyond this barrier.  // In Java volatile variables provide this guarantee implicitly.  // No direct equivalent to atomic_thread_fence is needed.  while (flag[1 - self].get() == 1 && turn.get() == 1 - self)  Thread.yield();  }  static void unlock(int self) {  flag[self].set(0);  }  static void func(int s) {  int i = 0;  int self = s;  System.out.println('Thread Entered: ' + self);  lock(self);  // Critical section (Only one thread can enter here at a time)  for (i = 0; i < MAX; i++)  ans++;  unlock(self);  }  public static void main(String[] args) {  // Initialize the lock  lockInit();  // Create two threads (both run func)  Thread t1 = new Thread(() -> func(0));  Thread t2 = new Thread(() -> func(1));  // Start the threads  t1.start();  t2.start();  try {  // Wait for the threads to end.  t1.join();  t2.join();  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println('Actual Count: ' + ans + ' | Expected Count: ' + MAX * 2);  } } 
Python
import threading flag = [0 0] turn = 0 MAX = 10**9 ans = 0 def lock_init(): # This function initializes the lock by resetting the flags and turn. global flag turn flag = [0 0] turn = 0 def lock(self): # This function is executed before entering the critical section. It sets the flag for the current thread and gives the turn to the other thread. global flag turn flag[self] = 1 turn = 1 - self while flag[1-self] == 1 and turn == 1-self: pass def unlock(self): # This function is executed after leaving the critical section. It resets the flag for the current thread. global flag flag[self] = 0 def func(s): # This function is executed by each thread. It locks the critical section increments the shared variable and then unlocks the critical section. global ans self = s print(f'Thread Entered: {self}') lock(self) for _ in range(MAX): ans += 1 unlock(self) def main(): # This is the main function where the threads are created and started. lock_init() t1 = threading.Thread(target=func args=(0)) t2 = threading.Thread(target=func args=(1)) t1.start() t2.start() t1.join() t2.join() print(f'Actual Count: {ans} | Expected Count: {MAX*2}') if __name__ == '__main__': main() 
JavaScript
class PetersonYieldLockMemoryFence {  static flag = [0 0];  static turn = 0;  static MAX = 1000000000;  static ans = 0;  // Function to acquire the lock  static async lock(self) {  PetersonYieldLockMemoryFence.flag[self] = 1;  PetersonYieldLockMemoryFence.turn = 1 - self;  // Asynchronous loop with a small delay to yield  while (PetersonYieldLockMemoryFence.flag[1 - self] == 1 &&  PetersonYieldLockMemoryFence.turn == 1 - self) {  await new Promise(resolve => setTimeout(resolve 0));  }  }  // Function to release the lock  static unlock(self) {  PetersonYieldLockMemoryFence.flag[self] = 0;  }  // Function representing the critical section  static func(s) {  let i = 0;  let self = s;  console.log('Thread Entered: ' + self);    // Lock the critical section  PetersonYieldLockMemoryFence.lock(self).then(() => {  // Critical section (Only one thread can enter here at a time)  for (i = 0; i < PetersonYieldLockMemoryFence.MAX; i++) {  PetersonYieldLockMemoryFence.ans++;  }    // Release the lock  PetersonYieldLockMemoryFence.unlock(self);  });  }  // Main function  static main() {  // Create two threads (both run func)  const t1 = new Thread(() => PetersonYieldLockMemoryFence.func(0));  const t2 = new Thread(() => PetersonYieldLockMemoryFence.func(1));  // Start the threads  t1.start();  t2.start();  // Wait for the threads to end.  setTimeout(() => {  console.log('Actual Count: ' + PetersonYieldLockMemoryFence.ans + ' | Expected Count: ' + PetersonYieldLockMemoryFence.MAX * 2);  } 1000); // Delay for a while to ensure threads finish  } } // Define a simple Thread class for simulation class Thread {  constructor(func) {  this.func = func;  }  start() {  this.func();  } } // Run the main function PetersonYieldLockMemoryFence.main(); 
C++
// mythread.h (A wrapper header file with assert statements) #ifndef __MYTHREADS_h__ #define __MYTHREADS_h__ #include  #include  #include  // Function to lock a pthread mutex void Pthread_mutex_lock(pthread_mutex_t *m) {  int rc = pthread_mutex_lock(m);  assert(rc == 0); // Assert that the mutex was locked successfully }   // Function to unlock a pthread mutex void Pthread_mutex_unlock(pthread_mutex_t *m) {  int rc = pthread_mutex_unlock(m);  assert(rc == 0); // Assert that the mutex was unlocked successfully }   // Function to create a pthread void Pthread_create(pthread_t *thread const pthread_attr_t *attr   void *(*start_routine)(void*) void *arg) {  int rc = pthread_create(thread attr start_routine arg);  assert(rc == 0); // Assert that the thread was created successfully } // Function to join a pthread void Pthread_join(pthread_t thread void **value_ptr) {  int rc = pthread_join(thread value_ptr);  assert(rc == 0); // Assert that the thread was joined successfully } #endif // __MYTHREADS_h__ 
C
// mythread.h (A wrapper header file with assert // statements) #ifndef __MYTHREADS_h__ #define __MYTHREADS_h__ #include  #include    #include  void Pthread_mutex_lock(pthread_mutex_t *m) {  int rc = pthread_mutex_lock(m);  assert(rc == 0); }   void Pthread_mutex_unlock(pthread_mutex_t *m) {  int rc = pthread_mutex_unlock(m);  assert(rc == 0); }   void Pthread_create(pthread_t *thread const pthread_attr_t *attr   void *(*start_routine)(void*) void *arg) {  int rc = pthread_create(thread attr start_routine arg);  assert(rc == 0); } void Pthread_join(pthread_t thread void **value_ptr) {  int rc = pthread_join(thread value_ptr);  assert(rc == 0); } #endif // __MYTHREADS_h__ 
Python
import threading import ctypes # Function to lock a thread lock def Thread_lock(lock): lock.acquire() # Acquire the lock # No need for assert in Python acquire will raise an exception if it fails # Function to unlock a thread lock def Thread_unlock(lock): lock.release() # Release the lock # No need for assert in Python release will raise an exception if it fails # Function to create a thread def Thread_create(target args=()): thread = threading.Thread(target=target args=args) thread.start() # Start the thread # No need for assert in Python thread.start() will raise an exception if it fails # Function to join a thread def Thread_join(thread): thread.join() # Wait for the thread to finish # No need for assert in Python thread.join() will raise an exception if it fails 

Излаз: 

Thread Entered: 1  
Thread Entered: 0
Actual Count: 2000000000 | Expected Count: 2000000000