Şimdi veri kilitleme olarak adlandırılan diğer kilitleme türünü tartışıyoruz.
Doğru.
Veri kilitlemeyi anlamak için, burada bir foo fonksiyonu olduğunu görebilirsiniz.
Ve bu foo fonksiyonunda, bir liste nesnesi üzerinde bazı işlemler gerçekleştirdiğimiz
kodun bir kısmı var, doğru.
Bu, öğrencilerin bir bağlantı listesi olabilir, çalışanların bir bağlantı listesi olabilir veya uygulamanızın
koruduğu herhangi bir nesne türünün bir bağlantı listesi olabilir.
Doğru.
Bu kritik bir bölüm çünkü bu liste nesnesi üzerinde bazı işlemler gerçekleştiriyoruz.
İşleminizde çalışan birkaç iş parçacığı olabileceğini ve birkaç iş parçacığının kodun çeşitli bölümlerinde bu liste
nesnesi üzerinde belirli işlemler gerçekleştirebileceğini düşünün.
Doğru.
Yani bu işlem herhangi bir şey olabilir.
Bağlantı listesine bir düğüm ekliyor olabilir.
Bir düğümü arıyor olabilir, bir düğümü siliyor olabilir ya da herhangi bir şey olabilir.
Değil mi?
Dolayısıyla kodun bu kısmı kritik bir bölümdür çünkü kodun diğer kısımlarında programın diğer
iş parçacıkları tarafından erişilen nesneye erişmeye çalışıyoruz.
Şu anda bu kritik bir bölüm olduğu için bunu görebilirsiniz.
Bu yüzden hiç düşünmeden, bu kritik bölümü muteksler kullanarak korumamız gerekir.
Bu yüzden bu kritik bölümü yine p thread mutex lock ve p thread mutex unlock API çağrıları arasına sıkıştıracağız.
Doğru.
Şimdi burada dikkat edilmesi gereken nokta, burada günlüğe kaydedilen ve günlüğü açılan muteksin liste nesnesinin
bir özelliği olduğudur.
Burada mutex nesnesi, üzerinde işlem yapılan veri yapısına aittir.
Doğru.
Böylece her veri yapısının kendi muteksiyle ilişkilendirildiğini ve bu muteksin bir koruma görevi gördüğünü
görebilirsiniz.
Böyle bir muteks, veri yapısını eş zamanlı iş parçacığından, sürecin birkaç iş parçacığı tarafından güvenli olmayan erişimden
korur.
Öyleyse bir örnek yardımıyla veri kaydını anlamaya çalışalım.
Diyelim ki uygulamanızda bir fonksiyon var ve bu fonksiyonun amacı
sadece a a olabilir değerine bağlı olarak listeden belirli bir düğümü aramak.
Bunun bir çalışanın çalışan kimliği olduğunu varsayalım veya ne tür bir liste geçirdiğinize bağlı olarak
öğrencinin rulo numarası olabilir.
Bu, çalışanların bağlı listesi veya öğrencilerin bağlı listesi olabilir.
Değil mi?
Böylece düğümü bağlı listeden kaldırırız ve sonra düğümü serbest bırakırız.
Şimdi birinci senaryoyu düşünün.
Birinci senaryo, listeden düğüm silmek olan bu özel API'yi çağıran bir T1 iş parçacığı olduğu ve aktarılan
liste nesnesinin bağlantılı bir öğrenci listesi olduğu anlamına gelir.
Doğru.
Ve A değerinin 25 olarak ayrıştırıldığını varsayalım.
Yani bu basitçe, 31'in bağlantı listesinden kural numarası örneğin 25 olan bir öğrenci kaydını silmeye çalıştığı
anlamına gelir.
Bu sadece bir örnek.
Ve bu API'yi çağıran başka bir konu da var.
Doğru.
Ve t bir ve t iki eşzamanlı bir şekilde yürütülmeye çalışıldı ve t iki iş parçacığının bu API'ye
aktardığı liste nesnesi, örneğin çalışanların bir listesidir.
Doğru.
Ve a değeri, diyelim ki bir çalışan kimliği olan 30'u temsil eder.
Böylece aynı kod parçasının T1 ve T2 iş parçacığı tarafından çağrıldığını, ancak T1 ve T2 iş parçacıklarının aslında farklı
liste nesneleri üzerinde işlem yaptığını görebilirsiniz.
Bu tamamen farklı veri yapılarıdır.
Doğru.
Dolayısıyla, T1 ve T2 iş parçacığı aynı kod parçasını çağırdığı sürece, ancak bu kod parçası tamamen
farklı nesneler üzerinde hareket ediyorsa, o zaman söz konusu kod T1 ve T2 iş parçacığı için kritik bir bölüm
değildir.
Doğru.
Bu durumda T1 ve T2 iş parçacıklarının eşzamanlı erişimi herhangi bir değişmezlik sorununa yol açmayacaktır.
Bunun nedeni T1 ve T2 iş parçacığının tamamen farklı nesneler üzerinde hareket ediyor olmasıdır.
Dene.
T1, öğrencilerin bağlantı listesinden bir düğümü silmeye çalışırken, T2 iş parçacığı çalışanların bağlantı listesinden
bir düğümü silmeye çalışmaktadır.
Yani burada sadece bir geliştirici veya programcı olarak koda bakarak, bu belirli kod parçasının
kritik bir bölüm olup olmadığını anlayamazsınız.
Ama şimdi başka bir senaryoyu ele alalım.
Şimdi ikinci senaryoda, bir numaralı tehdidin bu API'yi çağırdığını ve geçirdiği listenin bir öğrenci
listesi olduğunu varsayalım, değil mi?
Ve sıra numarası 25 olan bir öğrenci için bağlantı listesinden bir düğümü silmeye çalışıyor.
Doğru.
Benzer şekilde, aynı API ve iş parçacığı olan bu API'yi çağıran bir T iki iş parçacığımız var.
T2 yine aynı öğrenci bağlantı listesi üzerinde hareket etmektedir.
Doğru.
Ve diyelim ki t iki numaralı iş parçacığı bağlantı listesinden rulo numarası 30 olan bir düğümü silmeye
çalışıyor.
Doğru.
Burada o konuyu görebilirsiniz.
Bu durumda T1 ve t2 iş parçacığı aynı bağlantı listesi üzerinde hareket etmektedir.
Doğru.
T bir ve T iki aynı nesneler üzerinde hareket ettiği sürece, yani her ikisi de aynı nesneden bir düğüm silmeye
çalıştığı sürece.
Bu, bu kod parçasının artık kritik bir bölüm olarak davrandığı anlamına gelir.
T1 ve T2 iş parçacığının eş zamanlı erişimi bu durumda değişmezlik sorununa neden olacaktır.
Bunun nedeni, hem T1 hem de T2 iş parçacığının aynı nesne üzerinde yazma işlemi gerçekleştiriyor olmasıdır, bu da aynı
bağlantı listesi yazma işlemidir.
Şimdi birinci senaryo ile ikinci senaryo arasındaki farkı anladınız.
Birinci senaryoda, T1 tehdidi ve T2 tehdidi farklı liste nesneleri üzerinde hareket ediyordu ve bu nedenle
bu kod parçası kritik bir bölüm değildi.
Ancak ikinci senaryoda, her iki tehdit de aynı liste nesnesi üzerinde hareket eder ve bu nedenle bu kod parçası
kritik bölüm haline gelir.
Değil mi?
Dolayısıyla, sadece koda bakarak bir kod parçasının kritik
bir bölüm olup olmadığına karar veremezsiniz.
Bu kod parçasına erişen eşzamanlı iş parçacıklarının aslında aynı nesneler üzerinde mi yoksa farklı nesneler
üzerinde mi hareket ettiğini bilmek de önemlidir.
Doğru.
Yani bu örneği ele alalım.
Eğer kod kilitleme uyguluyorsanız, o zaman sorun ne olabilir?
Şimdilik, bu kodu kod kilitleme özelliğini kullanacak şekilde güncellediğimi görebilirsiniz.
Bir dosyada global bir mutex nesnesi bildirdim ve potansiyel kritik bölüm kodunu Pthread
mutex lock ve pthread mutex unlock API çağrıları arasına sıkıştırdım.
Doğru.
Bu yüzden bu örnekte yine kod kilitlemeyi kullanıyorum.
Şimdi buradaki sorunu görebilirsiniz.
Şimdi burada bir vaka düşünün.
T1 iş parçacığı ve T2 iş parçacığı aynı bağlantı listesi üzerinde hareket ederken, bir öğrencinin bağlantı listesi
diyelim.
Değil mi?
Böylece 31 bu belirli işlevi çalıştırdığında muteksi kilitleyecektir.
Şu anda muteksin T1 iş parçacığı tarafından kilitlenmiş olması.
Eğer t2 iş parçacığı bu API'yi çağırmaya çalışırsa, t2 iş parçacığı bu belirli satırda engellenecektir.
Doğru.
Şimdi T1 ve T2 iş parçacıklarının farklı liste nesneleri üzerinde çalıştığı bir senaryo düşünün.
T1 iş parçacığının öğrenci listesi ve iş parçacığı üzerinde hareket ettiğini varsayalım.
T2 çalışan listesine göre hareket etmektedir.
Doğru.
Ve her iki iş parçacığı da eşzamanlı olarak her iki bağlı listeden ilgili belirli bir düğümü silmek için bu özel
API'yi çağırır.
Doğru.
Şimdi t1 iş parçacığının bu API'yi çağırdığını ve t1 iş parçacığının bu belirli global muteksi kilitleyeceğini
varsayalım.
Doğru.
Ve hatta tehdit işini yapmaya devam ediyor.
Yani, bu noktada T1 tehdidi kritik bölümde yürütülürken,
diyelim ki T2 tehdidi de geldi ve bu API'yi çağırdı, değil mi?
Ve şimdi T2 tehdidi bu belirli global muteksin zaten kilitli olduğunu görecektir.
Bu da T2 tehdidinin talimatlarda engelleneceği anlamına gelir.
I5 sağda.
Yani bu, iki numaralı ipliğin burada bloke olacağı anlamına geliyor.
Değil mi?
İki numaralı iplik engellenecektir.
Ve T1 tehdidi kritik bölüm üzerinden yürütülmesini tamamlamadığı sürece.
Ve son olarak, T1 iş parçacığı muteksi serbest bıraktığında, kritik bölüme yalnızca T2 iş parçacığı girer.
Ancak şimdi burada ortaya çıkan basit bir soru, iki iş parçacığı çakışmayan işlemler gerçekleştirirken
neden kritik bölümü seri olarak yürütmek zorunda olduğunuzdur?
İki iş parçacığı çakışmayan işlemler gerçekleştiriyor çünkü iki iş parçacığı aslında farklı veri yapıları
üzerinde birlikte hareket ediyor.
Değil mi?
Bir iş parçacığının gereksiz yere başka bir iş parçacığını engellemesini istemezsiniz ve bundan hiçbir şey kazanmazsınız.
Değil mi?
Dolayısıyla bu örnekte, kod kilitlemenin aslında muteksleri kullanmanın doğru bir yolu olmadığını görebilirsiniz.
Burada muteksleri koda göre kullanmak, kilitleme hiç mantıklı değil.
Bu nedenle, burada muteksi kod kilitlemesine göre kullanmanın neden bir yol olmadığını açıkladıktan sonra.
Veri kilitlemenin ortaya çıkmasının nedeni budur.
Şuna bir bakalım.
Veri kilitlemenin aslında muteksleri kullanmanın doğru bir yolu olduğu ve bu örnekte karşılıklı dışlama sağladığı.
Veri günlüğü tutarken, programınızın kodunun bir kısmını günlüğe kaydetmek yerine veri yapısını günlüğe kaydetmeye
odaklanıyoruz.
Doğru.
Dolayısıyla nihai hedefimiz, bu API'nin T1 ve T2 tehditleri tarafından her iki iş parçacığı da aynı bağlantı listesini, diyelim
ki öğrencilerin bağlantı listesini güncelleyecek şekilde çağrılması durumunda, bu iş parçacıklarından
yalnızca birinin kritik bölüme girmesine izin verilmesi olmalıdır.
Değil mi?
Yani, T1 iş parçacığı kritik bölüme girerse, t2 iş parçacığı engellenmelidir ve bunun tersi
de geçerlidir.
Ancak başka bir senaryoda, aynı API T1 ve T2 iş parçacığı tarafından çağrılıyorsa, T1 iş parçacığı bir öğrenci bağlantı
listesi üzerinde ve T2 iş parçacığı da bir çalışan bağlantı listesi üzerinde çalışıyorsa, her iki iş parçacığının
da birbirini engellemeden çalışmasına izin verilmelidir.
Bu durumda ilk etapta kritik bir bölüm bulunmamaktadır.
Doğru.
Yani yapacağım şey, bu özel kod parçasını p thread, mutex lock ve p thread mutex unlock
API çağrıları arasında sandviçleyeceğim.
Buradaki tek fark, kilitleme ve kilit açma için kullanılan mutex nesnesinin, üzerinde
işlem yapılan veri yapısının sahip olduğu bir mutex nesnesi olmasıdır.
Doğru.
Burada mutex nesnesinin liste nesnesinin veri üyelerinden biri olduğunu görebilirsiniz.
Doğru.
Bu da basitçe T1 ve T2 iş parçacıklarının birbirlerini engelleyeceği ve kritik bölümde yalnızca bir iş parçacığının
çalışacağı anlamına gelir.
Yalnızca T1 ve T2 iş parçacığı aynı bağlantı listesinde hareket ettiğinde.
Doğru.
Ancak T1 ve T2 iş parçacığı farklı bağlı listeler üzerinde çalışıyorsa, T1 ve T2 iş parçacıklarının
her ikisi de farklı muteksleri kilitleyecektir.
Doğru, çünkü liste nesnesinin kendisi farklıdır.
Yani bu durumda T1 ve T2 kodun bu belirli parçasını eşzamanlı veya aynı anda yürütebilir.
Doğru.
Bu durumda, hem T1 hem de T2 iş parçacığı, üzerinde hareket ettikleri ilgili bağlı listeden bir düğümü gerçekten
arayabilir ve kaldırabilir.
Doğru.
Dolayısıyla, kritik bölümleri korumak için Mutex'leri kullandığınız çok iş parçacıklı programlar
yazarken, vakaların %90'ında kod kilitleme yerine veri kilitlemeyi kullanacaksınız.
Çoğu zaman Mutex'leri programınızın bir kod parçasını korumak yerine iş parçacıkları arasında paylaşılan verileri
korumak için kullanırız.