Semaforlar ve Muteksler

Semaforlar ve Muteksler, paylaşılan kaynaklara çoklu iş parçacığı erişimini senkronize etmek için kullanılan eşzamanlılık kontrolleridir.

Semafor

İşte bu Stackoverflow sorusundan harika bir açıklama:

Semaforları bir gece kulübünde fedai olarak düşünün. adanmış var kulüpte aynı anda izin verilen kişi sayısı. kulüp ise dolu kimsenin girmesine izin verilmez, ancak bir kişi ayrılır ayrılmaz başka bir kişi girebilir.

Bu sadece sayısını sınırlamanın bir yolu. belirli bir kaynak için tüketiciler. Örneğin, sayısını sınırlamak için bir uygulamadaki bir veritabanına eşzamanlı çağrılar.

Muteks

Bir muteks, 1’lik bir semafordur (yani, bir seferde yalnızca bir iş parçacığı). Gece kulübü metaforunu kullanarak, gece kulübündeki bir banyo tezgahı açısından bir muteks düşünün. Aynı anda yalnızca bir yolcuya izin verilir.

Java ve C++‘da Mutex

Java’nın bir Mutex sınıfı olmamasına rağmen, 1 Semaforunu kullanarak bir Mutex’i taklit edebilirsiniz. Aşağıdaki örnek, kilitli ve kilitsiz iki iş parçacığı yürütür. Kilitleme olmadan, program biraz rasgele bir çıktı karakteri ($ veya #) yayar. Kilitleme ile program, ##### veya $$$$$‘dan oluşan güzel, düzenli karakter kümeleri yayar, ancak asla # ve $ karışımı olmaz.

import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;

public class MutexTest {
   static Semaphore semaphore = new Semaphore(1);

   static class MyThread extends Thread {
      boolean lock;
      char c = ' ';

      MyThread(boolean lock, char c) {
         this.lock = lock;
         this.c = c;
      }

      public void run() {
         try {
            // Generate a random number between 0 & 50
            // The random nbr is used to simulate the "unplanned"
            // execution of the concurrent code
            int randomNbr = ThreadLocalRandom.current().nextInt(0, 50 + 1);

            for (int j=0; j<10; ++j) {
               if(lock) semaphore.acquire();
               try {
                  for (int i=0; i<5; ++i) {
                     System.out.print(c);
                     Thread.sleep(randomNbr);
                  }
               } finally {
                  if(lock) semaphore.release();
               }
               System.out.print('|');
            }
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   public static void main(String[] args) throws Exception {
      System.out.println("Without Locking:");
      MyThread th1 = new MyThread(false, '$');
      th1.start();
      MyThread th2 = new MyThread(false, '#');
      th2.start();
      
      th1.join();
      th2.join();

      System.out.println('\n');

      System.out.println("With Locking:");
      MyThread th3 = new MyThread(true, '$');
      th3.start();
      MyThread th4 = new MyThread(true, '#');
      th4.start();
      
      th3.join();
      th4.join();

      System.out.println('\n');
   }
}

javac MutexTest.java’yı çalıştırın; java MutexTest ve şunun gibi bir şey elde edeceksiniz:

Kilitlemesiz: #$$$$$|$$$$$|$$#$$$|$$$$$|$$$$#$|$$$$$|$$$$$|$#$$ $$|$$$$$|$$$#$$||#####|#####|#####|#####|#####|### ##|#####|#####|#####|

Kilitli: $$$$$|#####|$$$$$|#####|$$$$$|#####|$$$$$|#####|$ $$$$|#####|$$$$$|#####|$$$$$|#####|$$$$$|#####|$$$ $$|#####|$$$$$|#####|

İşte C++‘daki aynı örnek:

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex
#include <random>         // std::random_device

class MutextTest {
   private:
      static std::mutex mtx;  // mutex for critical section

   public:
      static void run(bool lock, char c) {
         // Generate a random number between 0 & 50
         // The random nbr is used to simulate the "unplanned"
         // execution of the concurrent code
         std::uniform_int_distribution<int> dist(0, 50);
         std::random_device rd;
         int randomNbr = dist(rd);
         //std::cout << randomNbr << '\n';

        for(int j=0; j<10; ++j) {
           if(lock) mtx.lock();
           for (int i=0; i<5; ++i) {
              std::cout << c << std::flush;
              std::this_thread::sleep_for(std::chrono::milliseconds(randomNbr));
           }
           std::cout << '|';
           if(lock) mtx.unlock();
        }
      }
};

std::mutex MutextTest::mtx;

int main()
{
  std::cout << "Without Locking:\n";
  std::thread th1 (MutextTest::run, false, '$');
  std::thread th2 (MutextTest::run, false, '#');

  th1.join();
  th2.join();

  std::cout << "\n\n";

  std::cout << "With Locking:\n";
  std::thread th3 (MutextTest::run, true, '$');
  std::thread th4 (MutextTest::run, true, '#');

  th3.join();
  th4.join();

  std::cout << '\n';

  return 0;
}

g++ –std=c++11 MutexTest.cpp’yi çalıştırın; ./a.out ve şunun gibi bir şey elde edeceksiniz:

Kilitlemesiz: $#$#$#$#$#|$|#$#$#$#$#|$$|#$#$#$#|$#$|#$#$#$#|$$# $|#$#$#|$#$#$|#$#$#|$#$$#$|#$#|$#$#$#$|#$#|$#$#$#$ $|#|$#$#$#$#$|#|####|

Kilitli: $$$$$|#####|$$$$$|#####|$$$$$|#####|$$$$$|#####|$ $$$$|#####|$$$$$|#####|$$$$$|#####|$$$$$|#####|$$$ $$|#####|$$$$$|#####|