@@ -167,12 +167,223 @@ nesneleri de bir vektörde toplanır ve son aşamada tamamının sonuçları ür
167167
168168## Atomic Reference Counting
169169
170- // todo@buraksenyurt Not Implemented Yet
170+ Arc türününün kullanımını anlamak için öncelikle sorunu ortaya koymamız gerekir. Aşağıdaki örnek kod parçasını göz önüne
171+ alalım.
172+
173+ ``` rust
174+ use std :: thread;
175+
176+ fn main () {
177+ run_with_error ();
178+ println! (" After the thread calling" );
179+ }
180+
181+ pub fn run_with_error () {
182+ let data = vec! [
183+ " Service Red: Task A" ,
184+ " Service Blue: Task B" ,
185+ " Service Green: Task C" ,
186+ " Service Alpha: Task D" ,
187+ ];
188+
189+ let mut handles = vec! [];
190+ for i in 0 .. 2 {
191+ let handle = thread :: spawn (move || {
192+ for task in & data {
193+ println! (" Thread '{}' is processing '{}'" , i , task );
194+ }
195+ });
196+
197+ handles . push (handle );
198+ }
199+
200+ for handle in handles {
201+ handle . join (). unwrap ();
202+ }
203+ }
204+ ```
205+
206+ Bu kod derlenmeyecek ve aşağıdaki hata mesajı üretilecektir.
207+
208+ ``` text
209+ let data = vec![
210+ ---- move occurs because `data` has type `Vec<&str>`, which does not implement the `Copy` trait
211+
212+ for i in 0..2 {
213+ ------------- inside of this loop
214+ let handle = thread::spawn(move || {
215+ ^^^^^^^ value moved into closure here, in previous iteration of loop
216+ for task in &data {
217+ ---- use occurs due to use in closure
218+ ```
219+
220+ Senaryoda data isimli vektörü kullanmak isteyen 2 farklı thread oluşturulmak istenmektedir. Sorun aynı anda birden fazla
221+ thread'in ortak bir referansa erişmeye çalışmasıdır zira data değişkeninin içeriği closure ifadesi içerisinde
222+ alındığında sahiplikte bu bloğa geçer. Dolayısıyla Rust'ın sahiplik ilkeleri gereği thread safe bir ortamın sağlanması
223+ için bu referans veri başka bir thread tarafından kullanılamaz. Çözüm olarak Arc türünden yararlanılabilir. Aşağıdaki
224+ kod örneğinde bu kullanıma yer verilmektedir.
225+
226+ ``` rust
227+ use std :: sync :: Arc ;
228+ use std :: thread;
229+
230+ fn main () {
231+ run_correctly ();
232+ println! (" After the thread calling" );
233+ }
234+
235+ pub fn run_correctly () {
236+ let data = Arc :: new (vec! [
237+ " Service Red: Task A" ,
238+ " Service Blue: Task B" ,
239+ " Service Green: Task C" ,
240+ " Service Alpha: Task D" ,
241+ ]);
242+
243+ let mut handles = vec! [];
244+
245+ for i in 0 .. 2 {
246+ let data_clone = Arc :: clone (& data );
247+ let handle = thread :: spawn (move || {
248+ for task in data_clone . iter () {
249+ println! (" Thread '{}' is processing '{}'" , i , task );
250+ }
251+ });
252+
253+ handles . push (handle );
254+ }
255+
256+ for handle in handles {
257+ handle . join (). unwrap ();
258+ }
259+ }
260+ ```
261+
262+ Bir önceki örnekten farklı olarak data isimli değişken oluşturulurken vektörün kendisi yeni bir atomik referans sayacına
263+ devredilir. İkinci önemli nokta thread açılmadan önce herbir thread bloğuna bu Arc referansının bir klonunun
264+ atanmasıdır. clone metodu çağırılırken data değişkeninin referansının verildiğine de dikkat edilmelidir. Böylece data
265+ değişkenin işaret ettiği veriye her thread güvenli bir şekilde erişir, Arc söz konusu referans klonlarını sayar ve drop
266+ sırasında da bunları sondan başa doğru kaldırır. Ne var ki thread'lerin veriye güvenli erişimi söz konusu olsa da bazı
267+ senaryolarda verinin tutarlılığının bozulma ihtimali vardır.
268+
269+ Şimdiki senaryoda thread'lerin aynı veri üzerinde değişiklik yapmak istediğiniz düşünelim. Bu amaçla koduuzu biraz
270+ değiştirip aşağıdaki hale getirelim.
271+
272+ ``` rust
273+ use std :: sync :: Arc ;
274+ use std :: thread;
275+
276+ fn main () {
277+ run_inconsistent ();
278+ println! (" After the thread calling" );
279+ }
280+
281+ pub fn run_inconsistent () {
282+ let data = Arc :: new (vec! [0 ; 10 ]);
283+ let mut handles = vec! [];
284+
285+ for i in 0 .. 4 {
286+ let data_clone = Arc :: clone (& data );
287+ let handle = thread :: spawn (move || {
288+ for j in 0 .. data_clone . len () {
289+ data_clone [j ] += 2 ;
290+ println! (" Thread {} updating index {}" , i , j );
291+ }
292+ });
293+
294+ handles . push (handle );
295+ }
296+
297+ for handle in handles {
298+ handle . join (). unwrap ();
299+ }
300+
301+ println! (" {:?}" , * data );
302+ }
303+ ```
304+
305+ Bu kod parçası da derlenmeyecek ve aşağıdaki hatayı üretecektir.
306+
307+ ``` text
308+ error[E0596]: cannot borrow data in an `Arc` as mutable
309+ --> Lesson_11\src\main.rs:17:17
310+ |
311+ 17 | data_clone[j] += 2;
312+ | ^^^^^^^^^^ cannot borrow as mutable
313+ |
314+ = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Vec<i32>>`
315+ ```
316+
317+ Arc referansının klonu alınarak üzerinde değişiklik yapılmak istenmektedir ancak Arc veriye eş zamanlı olarak güvenli
318+ erişim sağlarken onun mutable olmasına izin vermez. Bir başka deyişle veriye eş zamanlı erişim Arc kullanımı ile
319+ mümkünken güvenli bir şekilde değiştirilmesi için farklı bir metodoloji kullanmamız gerekir. Buna istinaden aşağıdaki
320+ örneği göz önüne alabiliriz.
321+
322+ ``` rust
323+ use std :: sync :: {Arc , Mutex };
324+ use std :: thread;
325+ use std :: time :: Duration ;
326+
327+ fn main () {
328+ run_mutex ();
329+ println! (" After the thread calling" );
330+ }
331+
332+ pub fn run_mutex () {
333+ let data = Arc :: new (Mutex :: new (0 ));
334+
335+ let data_clone_one = Arc :: clone (& data );
336+ let t1 = thread :: spawn (move || {
337+ let mut num = data_clone_one . lock (). unwrap ();
338+ * num += 3 ;
339+ println! (" Thread 1 has locked the data." );
340+ thread :: sleep (Duration :: from_secs (3 )); // Kasıtlı olarak bekletme yapıyoruz
341+ println! (" After 3 seconds...\ n Thread 1 is unlocking the data." );
342+ });
343+
344+ let data_clone_two = Arc :: clone (& data );
345+ let t2 = thread :: spawn (move || {
346+ println! (" Thread 2 is trying to lock the data." );
347+ let mut num = data_clone_two . lock (). unwrap ();
348+ * num += 5 ;
349+ println! (" Thread 2 has locked and updated the data." );
350+ });
351+
352+ t1 . join (). unwrap ();
353+ t2 . join (). unwrap ();
354+
355+ println! (" Final value: {}" , * data . lock (). unwrap ());
356+ }
357+ ```
358+
359+ Değiştirilmek istenen veri öncelikle bir ** Mutex** nesnesinin koruması altına bırakılır ve ** Arc** smart pointer'ı ile
360+ ilişkilendirilir. Buna göre farklı thread'ler söz konusu veriye güvenli erişebilir ama değiştirmek istediklerinde bir
361+ kilit mekanizması koyarak diğer thread'lerin aynı veriyi değiştirmesini engeller. ** Thread** 'ler bir ** closure** ifadesi
362+ ile başlatıldığında kilit mekanizması bu blok içerisinde konur ve scope dışına gelindiğinde söz konusu kilit otomatik
363+ olarak düşer. Bu nedenle yukarıdaki örnek kod parçası sorunsuz ve ** thread-safe** bir şekilde çalışır. Örnek kod
364+ parçasında gerçekten kilit mekanizmalarının çalıştığını gözlemlemek için bazı bildirimler eklenmiş ve hayali bekleme
365+ süresi konulmuştur. Buna göre beklenen çalışma zamanı çıktısı aşağıdaki gibidir.
366+
367+ ![ mutex runtime.png] ( mutexRuntime.png )
171368
172369## Thread Poisoning
173370
174371// todo@buraksenyurt Not Implemented Yet
175372
176373## Concurrency vs Parallel Programming
177374
178- // todo@buraksenyurt Not Implemented Yet
375+ Eş zamanlılık _ (Concurrency)_ ve paralel programlama sıksık birbirlerine karıştırılırlar. ** Concurrency** genel olarak
376+ birden fazla işin aynı anda başlatılmas ve yönetilmesi olarak tanımlanır. Fakat birden fazla işin fiziksel olarak aynı
377+ anda işletilmesi paralel programlama olarak tanımlanır. ** Concurrency** , başlatılan görevlerin birbirine karışmadan
378+ yönetilmesine odaklanırken paralel programlamada işlerin gerçekten fiziksel kaynaklar arasında bölüşülerek
379+ hızlandırılması esastır. Dolayısıyla paralel programlama da donanım yani çekirdek sayıları öne çıkar. Görüldüğü üzere
380+ tanımlar birbirine çok yakındır bu nedenle karıştırılmaları da doğaldır. ** Concurrency** de işler sırasıyla veya
381+ birbirine bağımlı olmadan işletilirken genellikle yardımcı bazı küfeler kullanılır. ** async/await** kullanımları, *
382+ * Future** ve ** tokio** gibi kütüphaneler concurrent programlama için kullanılır. Paralel programlama çoğunlukla işletim
383+ sistemi seviyesinde ve thread enstrümanı kullanılarak icra edilir. Rust tarafında spawn metodu basitçe thread başlatmak
384+ için yeterlidir ancak işlemci çekirdeklerinden daha iyi yararlanabilmek için ** rayon** gibi harici küfeler _ (crates)_
385+ kullanılır. Asenkron programlama verilebilecek en güzel örneklerden birisi web sunucusudur. Web sunucuları eş zamanlı
386+ olarak gelen sayısız isteği karşılamakla yükümlüdür. Buradaki işlevsellik paralel programladan ziyade gelen taleplerin
387+ asenkron olarak belli bir planlayıcı dahilinde birbirlerini beklemden yürütülebilmesidir. Bir başka eş zamanlı çalışma
388+ örneği de asenkron icra edilen ** I/O** işlemleridir. Paralel programlama ileri seviye hesaplamalar gereken süreçlerde
389+ veya büyük verilerin işlenmesi hatta geniş dil modellerinde çok daha etkilidir.
0 commit comments