@@ -167,12 +167,223 @@ nesneleri de bir vektörde toplanır ve son aşamada tamamının sonuçları ür
167
167
168
168
## Atomic Reference Counting
169
169
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 )
171
368
172
369
## Thread Poisoning
173
370
174
371
// todo@buraksenyurt Not Implemented Yet
175
372
176
373
## Concurrency vs Parallel Programming
177
374
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