Skip to content

Commit de360a1

Browse files
committed
Updated the chapter about threads.
1 parent 2fe8a76 commit de360a1

File tree

8 files changed

+267
-38
lines changed

8 files changed

+267
-38
lines changed

Lesson_11/README.md

+223-3
Original file line numberDiff line numberDiff line change
@@ -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

375595
Eş zamanlılık _(Concurrency)_ ve paralel programlama sıksık birbirlerine karıştırılırlar. **Concurrency** genel olarak
376596
birden fazla işin aynı anda başlatılmas ve yönetilmesi olarak tanımlanır. Fakat birden fazla işin fiziksel olarak aynı

Lesson_11/recoveryThread.png

28.4 KB
Loading

Lesson_11/src/main.rs

+30-21
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
1+
use std::fs::File;
2+
use std::io::Write;
13
use std::sync::{Arc, Mutex};
24
use std::thread;
3-
use std::time::Duration;
45

56
fn main() {
6-
run_mutex();
7-
println!("After the thread calling");
7+
poisoning_case_logging();
8+
println!("Everything is good!");
89
}
10+
pub fn poisoning_case_logging() {
11+
let log_file = Arc::new(Mutex::new(
12+
File::create("system.log").expect("Unable to create log file"),
13+
));
14+
let log_file_clone = Arc::clone(&log_file);
915

10-
pub fn run_mutex() {
11-
let data = Arc::new(Mutex::new(0));
16+
let handle = thread::spawn(move || {
17+
let mut file = log_file_clone.lock().unwrap();
18+
writeln!(file, "Thread 1: Writing the system health status").unwrap();
19+
panic!("Errors occurred while writing to the log file!");
20+
});
1221

13-
let data_clone_one = Arc::clone(&data);
14-
let t1 = thread::spawn(move || {
15-
let mut num = data_clone_one.lock().unwrap();
16-
*num += 3;
17-
println!("Thread 1 has locked the data.");
18-
thread::sleep(Duration::from_secs(3)); // Kasıtlı olarak bekletme yapıyoruz
19-
println!("After 3 seconds...\nThread 1 is unlocking the data.");
22+
let log_file_clone = Arc::clone(&log_file);
23+
let handle_2 = thread::spawn(move || {
24+
let mut file = log_file_clone.lock().unwrap();
25+
thread::sleep(std::time::Duration::from_secs(3));
26+
writeln!(file, "Thread 2: Attempting to write").unwrap();
2027
});
2128

22-
let data_clone_two = Arc::clone(&data);
23-
let t2 = thread::spawn(move || {
24-
println!("Thread 2 is trying to lock the data.");
25-
let mut num = data_clone_two.lock().unwrap();
26-
*num += 5;
27-
println!("Thread 2 has locked and updated the data.");
29+
let log_file_clone = Arc::clone(&log_file);
30+
let recovery_handle = thread::spawn(move || {
31+
let mut file = log_file_clone
32+
.lock()
33+
.unwrap_or_else(|poisoned| poisoned.into_inner());
34+
thread::sleep(std::time::Duration::from_secs(3));
35+
writeln!(file, "Thread 2: Recovering from poisoned state").unwrap();
2836
});
2937

30-
t1.join().unwrap();
31-
t2.join().unwrap();
38+
let _ = handle.join();
39+
let _ = handle_2.join();
40+
let _ = recovery_handle.join();
3241

33-
println!("Final value: {}", *data.lock().unwrap());
42+
println!("Log file operations completed");
3443
}

Lesson_11/threadPoisoning.png

32.6 KB
Loading

drone-lab/src/controller/drone_repository.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ impl DroneRepository {
5454
}
5555

5656
/// # Kaydetme Hata Enum'u
57-
///
57+
///
5858
/// Drone kaydetme işlemi sırasında oluşabilecek hataları temsil eder.
59-
///
59+
///
6060
/// ## Enum Variants
61-
///
61+
///
6262
/// * `InvalidDroneId` - Geçersiz drone ID hatası
6363
/// * `WrongModelName` - Yanlış model adı hatası
6464
#[derive(Debug, PartialEq)]

drone-lab/src/controller/flight_controller.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ impl FlightController {
5555
}
5656

5757
/// # Drone Durum Enum'u
58-
///
58+
///
5959
/// Drone'un durumunu temsil eden enum'dur.
60-
///
60+
///
6161
/// ## Enum Variants
62-
///
62+
///
6363
/// * `OutOffRange(Location)` - Drone'un uçuş alanının dışına çıktığını belirtir.
6464
/// * `Offline` - Drone'un çevrimdışı olduğunu belirtir.
6565
/// * `LowBattery(BatteryRate)` - Drone'un pil seviyesinin düşük olduğunu belirtir.
@@ -74,15 +74,15 @@ pub enum DroneStatus<'a> {
7474
}
7575

7676
/// # Pil Seviyesi Yapısı
77-
///
77+
///
7878
/// Pil seviyesini temsil eden yapıdır.
79-
///
79+
///
8080
/// ## Fields
81-
///
81+
///
8282
/// * `0` - Pil seviyesini temsil eden f32 değeridir.
83-
///
83+
///
8484
/// ## Links
85-
///
85+
///
8686
/// Bu konuda [Primitive Obsession](https://refactoring.guru/smells/primitive-obsession) makalesinden
8787
/// daha detaylı bilgi alınabilir.
8888
#[derive(Debug, PartialEq)]

drone-lab/src/controller/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Bu modül, drone simülasyonunu ve kontrolünü sağlayan yapıları içerir.
2-
//!
2+
//!
33
//! ## Modüller
4-
//!
4+
//!
55
//! * `drone_repository` - Veritabanı CRUD operasyonlarını içerir.
66
//! * `flight_controller` - Drone'ların uçuş kontrollerini sağlar.
77
//! * `simulation_controller` - Drone sahası ile ilgili simülasyonları yönetir.

drone-lab/src/controller/simulation_controller.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,4 @@ impl Default for SimulationController<'_> {
214214
fn default() -> Self {
215215
Self::new()
216216
}
217-
}
217+
}

0 commit comments

Comments
 (0)