@@ -366,11 +366,231 @@ süresi konulmuştur. Buna göre beklenen çalışma zamanı çıktısı aşağ
366
366
367
367
![ mutex runtime.png] ( mutexRuntime.png )
368
368
369
- ## Thread Poisoning
369
+ ## Deadlock ve Thread Poisoning Problemleri
370
370
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.
372
375
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
374
594
375
595
Eş zamanlılık _ (Concurrency)_ ve paralel programlama sıksık birbirlerine karıştırılırlar. ** Concurrency** genel olarak
376
596
birden 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