@@ -366,11 +366,231 @@ süresi konulmuştur. Buna göre beklenen çalışma zamanı çıktısı aşağ
366366
367367![ mutex runtime.png] ( mutexRuntime.png )
368368
369- ## Thread Poisoning
369+ ## Deadlock ve Thread Poisoning Problemleri
370370
371- // todo@buraksenyurt Not Implemented Yet
371+ Her ne kadar rust dili ** thread-safe** bir ortam sağlamak için bazı kuralları devreye alsa da ** deadlock** veya ** mutex
372+ poisoning** gibi durumlardan kaçılamayabilir. Aşağıdaki örnek kodlarda bu durumlar ele alınmaktadır. Kilit
373+ mekanizmalarının hatalı kullanımları ** deadlock** oluşmasına sebep olur. Diğer yandan bir kilit söz konusu iken
374+ bulunulan thread'de ** panik** oluşması da sorun yaratır ve bu durum ** Thread Poisoning** olarak adlandırılır.
372375
373- ## Concurrency vs Parallel Programming
376+ ### Deadlock Durumu
377+
378+ Bu durumu ele almak için aşağıdaki kod parçasını göz önüne alalım.
379+
380+ ``` rust
381+ use std :: sync :: {Arc , Mutex };
382+ use std :: thread;
383+ fn main () {
384+ deadlock_case ();
385+ println! (" After the thread calling" );
386+ }
387+ pub fn deadlock_case () {
388+ let number = Arc :: new (Mutex :: new (1 ));
389+ let mut handles = vec! [];
390+
391+ for i in 0 .. 10 {
392+ let number = Arc :: clone (& number );
393+ let handle = thread :: spawn (move || {
394+ println! (" For counter is {}" , i );
395+ let mut num = number . lock (). unwrap ();
396+ let mut another_num = number . lock (). unwrap (); // Tuzak satır
397+ * num += 1 ;
398+ });
399+ handles . push (handle );
400+ }
401+
402+ for handle in handles {
403+ println! (" Joining handle" );
404+ handle . join (). unwrap ();
405+ }
406+
407+ println! (" {:?}" , number . lock (). unwrap ());
408+ }
409+ ```
410+
411+ Senaryoya göre 10 farklı thread başlatılır ve number nesnesi üzerinde erişilen sayısal değer üzerinden işlem yapar.
412+ Mutex ve Arc kullanıldığı için thread-safe okuma ve değiştirme söz konusudur. Ancak bilerek tuzak bir satır eklenmiştir.
413+ Büyük çaplı projelerde bu durum kolayca gözden kaçabilir. İlk lock konulduktan sonra eklenen bir diğer lock thread'lerin
414+ birbirine beklemesine neden olacak ve bu bir deadlock'a sebebiyet verecektir. Zira örnek çalıştırıldığında sonlanmadığı
415+ açıkça görülebilir. Durumu daha gerçekeçi bir senaryo üzerinden pekiştirelim. Bu sefer bir banka hesap bilgisindeki
416+ bakiye alanı üzerinden işlem yapılmakta.
417+
418+ ``` rust
419+ use std :: sync :: {Arc , Mutex };
420+ use std :: thread;
421+ fn main () {
422+ deadlock_case_banking ();
423+ println! (" After the thread calling" );
424+ }
425+ struct Account {
426+ owner : String ,
427+ balance : f32 ,
428+ }
429+
430+ pub fn deadlock_case_banking () {
431+ let my_account = Arc :: new (Mutex :: new (Account {
432+ owner : " John Doe" . to_string (),
433+ balance : 100.0 ,
434+ }));
435+ let other_account = Arc :: new (Mutex :: new (Account {
436+ owner : " Merry Jane" . to_string (),
437+ balance : 200.0 ,
438+ }));
439+
440+ let my_account_clone = Arc :: clone (& my_account );
441+ let other_account_clone = Arc :: clone (& other_account );
442+
443+ let handle1 = thread :: spawn (move || {
444+ let mut source_account = my_account_clone . lock (). unwrap ();
445+ println! (" Thread 1: Locked by source account" );
446+ thread :: sleep (std :: time :: Duration :: from_secs (1 ));
447+ let mut target_account = other_account_clone . lock (). unwrap ();
448+ println! (" Thread 1: Locked by target account" );
449+
450+ source_account . balance -= 50.0 ;
451+ target_account . balance += 50.0 ;
452+ });
453+
454+ let my_account_clone = Arc :: clone (& my_account );
455+ let other_account_clone = Arc :: clone (& other_account );
456+
457+ let handle2 = thread :: spawn (move || {
458+ let mut acc2 = other_account_clone . lock (). unwrap ();
459+ println! (" Thread 2: Locked by target account" );
460+ thread :: sleep (std :: time :: Duration :: from_secs (1 ));
461+ let mut acc1 = my_account_clone . lock (). unwrap ();
462+ println! (" Thread 2: Locked by source account" );
463+
464+ acc2 . balance -= 25.0 ;
465+ acc1 . balance += 25.0 ;
466+ });
467+
468+ handle1 . join (). unwrap ();
469+ handle2 . join (). unwrap ();
470+
471+ println! (
472+ " Final balances: My Account : {}, Other Account: {}" ,
473+ my_account . lock (). unwrap (). balance,
474+ other_account . lock (). unwrap (). balance
475+ );
476+ }
477+ ```
478+
479+ Account isimli veri yapısı sembolik olarak hesap bilgilerini tutar. İki farklı hesap nesnesi tanımlanır ve thread'lerde
480+ güvenli şekilde ele alınabilmeleri için Arc, Mutex enstrümanları ile sarmalanır. handle1 ve handle2 isimli JoinHandle
481+ türevleri iki ayrı thread başlatır. Her iki thread kendi içerisinde ilgili değişkenler için kilit koyar. Devam eden
482+ kısımda ise thread'ler birbirini kitler ve deadlock durumu oluşur.
483+
484+ ### Thread Poisoning
485+
486+ Thread'ler işletildiğinde olası durumlardan birisi de thread içerisinde panik oluşmasıdır. Aşağıdaki kod parçasını
487+ göz önüne alalım.
488+
489+ ``` rust
490+ use std :: fs :: File ;
491+ use std :: io :: Write ;
492+ use std :: sync :: {Arc , Mutex };
493+ use std :: thread;
494+
495+ fn main () {
496+ poisoning_case_logging ();
497+ }
498+ pub fn poisoning_case_logging () {
499+ let log_file = Arc :: new (Mutex :: new (
500+ File :: create (" system.log" ). expect (" Unable to create log file" ),
501+ ));
502+ let log_file_clone = Arc :: clone (& log_file );
503+
504+ let handle = thread :: spawn (move || {
505+ let mut file = log_file_clone . lock (). unwrap ();
506+ writeln! (file , " Thread 1: Writing the system health status" ). unwrap ();
507+ panic! (" Errors occurred while writing to the log file!" );
508+ });
509+
510+ let log_file_clone = Arc :: clone (& log_file );
511+ let handle_2 = thread :: spawn (move || {
512+ let mut file = log_file_clone . lock (). unwrap ();
513+ thread :: sleep (std :: time :: Duration :: from_secs (3 ));
514+ writeln! (file , " Thread 2: Attempting to write" ). unwrap ();
515+ });
516+
517+ let _ = handle . join ();
518+ let _ = handle_2 . join ();
519+
520+ println! (" Log file operations completed" );
521+ }
522+ ```
523+
524+ Senaryoya göre disk üzerindeki bir dosyaya farklı thread'ler log bilgisi yazmaya çalışmaktadır. Gerçek hayatta sıklıkla
525+ karşılaşılabilecek bir işlem olduğunu ifade edebiliriz. İlk thread açıldığında dosya yazma işlemi gerçekleştirir. Burada
526+ kilit mekanizması kullanıldığından farklı thread'lerin ayno dosya içeriğine yazması mümkündür. Ancak disk fiziki bir
527+ donanım olduğundan tahmin edilemeyen sorunlar oluşabilir. Söz gelimi diske erişim geçici süre ortadan kalkar, disk
528+ dolmuştur vs Bunlar bir panik oluşması için yeterlidir. Örnekte kasıltı olarak bir panic oluşturulur. Çalışma zamanı
529+ çıktısı aşağıdaki gibidir.
530+
531+ ![ Poisoning.png] ( threadPoisoning.png )
532+
533+ İlk thread system.log isimli bir dosya açmış ve içerisine bir log bırakmıştır ancak sonrasında bir panik oluşmuştur.
534+ Dolayısıyla bu thread zehirlenmiş ve ana thread'e doğru bir panik fırlatmıştır. Dolayısıyla ikinci thread log dosyasına
535+ yazamaz zira program sonlanır. Bu gibi durumların önüne geçmek için recovery thread'ler kullanılıp unwrap_or_else
536+ metodları ile panik durumu kontrol altına alınabilir. Örneği aşağıdaki şekilde değiştirdiğimizi düşünelim.
537+
538+ ``` rust
539+ use std :: fs :: File ;
540+ use std :: io :: Write ;
541+ use std :: sync :: {Arc , Mutex };
542+ use std :: thread;
543+
544+ fn main () {
545+ poisoning_case_logging ();
546+ println! (" Everything is good!" );
547+ }
548+ pub fn poisoning_case_logging () {
549+ let log_file = Arc :: new (Mutex :: new (
550+ File :: create (" system.log" ). expect (" Unable to create log file" ),
551+ ));
552+ let log_file_clone = Arc :: clone (& log_file );
553+
554+ let handle = thread :: spawn (move || {
555+ let mut file = log_file_clone . lock (). unwrap ();
556+ writeln! (file , " Thread 1: Writing the system health status" ). unwrap ();
557+ panic! (" Errors occurred while writing to the log file!" );
558+ });
559+
560+ let log_file_clone = Arc :: clone (& log_file );
561+ let handle_2 = thread :: spawn (move || {
562+ let mut file = log_file_clone . lock (). unwrap ();
563+ thread :: sleep (std :: time :: Duration :: from_secs (3 ));
564+ writeln! (file , " Thread 2: Attempting to write" ). unwrap ();
565+ });
566+
567+ let log_file_clone = Arc :: clone (& log_file );
568+ let recovery_handle = thread :: spawn (move || {
569+ let mut file = log_file_clone
570+ . lock ()
571+ . unwrap_or_else (| poisoned | poisoned . into_inner ());
572+ thread :: sleep (std :: time :: Duration :: from_secs (3 ));
573+ writeln! (file , " Thread 2: Recovering from poisoned state" ). unwrap ();
574+ });
575+
576+ let _ = handle . join ();
577+ let _ = handle_2 . join ();
578+ let _ = recovery_handle . join ();
579+
580+ println! (" Log file operations completed" );
581+ }
582+ ```
583+
584+ İlk thread yine zehirlenir ve ikinci thread'in çalışmasını engeller ancak recovery modundaki son thred kilitlenmiş nesne
585+ referansını unwarp_or_else metodu ile ele alır. Bu metod hata durumunda alternatif bir çıktı üretilmesini garanti eder.
586+ Dolayısıyla recovery thread içerisinden söz konusu log dosyasına bilgi yazdırılır. Programın çalışma zamanı çıktısı
587+ aşağıdaki gibi olacaktır.
588+
589+ ![ Recovery Thread.png] ( recoveryThread.png )
590+
591+ Bu tip log yazma operasyonları için ideal senaryo asenkron programlama taktiklerini kullanmaktır.
592+
593+ ## Concurrency ve Parallel Programming
374594
375595Eş zamanlılık _ (Concurrency)_ ve paralel programlama sıksık birbirlerine karıştırılırlar. ** Concurrency** genel olarak
376596birden fazla işin aynı anda başlatılmas ve yönetilmesi olarak tanımlanır. Fakat birden fazla işin fiziksel olarak aynı
0 commit comments