1
1
# Ders 12: Channels
2
2
3
- Bazı durumlarda thread'ler arasında veri aktarımı gerekebilir. Channel enstrümanı farklı thread'ler arasında veri
4
- taşınması için kullanılmaktadır . Rust standart kütüphanesi varsayılan olarak ** Multi Producer - Single Consumer** türünü
5
- destekler. Farklı senaryolar için tokio, crossbeam gibi küfelerden de yararlanılabilir. ** MPSC** senaryosunda producer
6
- yani veri yayımını gerçekleştiren n sayıda iş parçacığı varken tek bir tüketici ya da dinleyici söz konusudur. Kanallar
7
- aşağıdaki gibi senaryolarda sıklıkla tercih edilir;
8
-
9
- - Görev _ (task)_ sonuçlarının ana bir thread içerisine toplanması
10
- - HTTP isteklerinin paralel olarak işlenmesi
3
+ Bazı durumlarda thread'ler arasında veri aktarımı gerekebilir. ** Channel** enstrümanı farklı thread'ler arasında veri
4
+ taşınması için kullanılır . Rust standart kütüphanesi varsayılan olarak ** Multi Producer - Single Consumer** türünü
5
+ destekler. Farklı senaryolar için ** tokio** , ** crossbeam** gibi küfelerden de yararlanılabilir. ** MPSC** senaryosunda
6
+ producer yani veri yayımını gerçekleştiren n sayıda iş parçacığı varken tek bir tüketici ya da dinleyici söz konusudur.
7
+ Kanallar aşağıdaki örnek senaryolarda sıklıkla tercih edilir;
8
+
9
+ - Görev _ (task)_ sonuçlarının ana bir ** thread** içerisine toplanması
10
+ - ** HTTP** isteklerinin paralel olarak işlenmesi
11
11
- Sistemde gerçekleşen olayların ana bir döngüye yönlendirilmesi
12
12
- GUI _ (Graphic User Interface)_ olaylarının merkezi bir yürütücüye aktarılması
13
13
- Okuma → işleme → yazma akışının parçalanarak thread’lere dağıtılması
14
14
- Merkezi log toplayıcı yapılar
15
15
16
- Kanallarda sahiplik _ (Ownership)_ kurallarına uygunluk vardır. Veri gönderildiğinde, alıcı taraf bunu recv() veya iter()
17
- fonksiyonları ile alır. Sahiplik göndericiden devralınır. Asenkron ve senkron uyarlamaları yazmak mümkündür. ** Tokio **
18
- veya ** async-std** gibi asenkron çalışma ortamları için defacto haline gelmiş olan ** tokio::sync::mpsc ** küfesi
19
- kullanılır. Performans gereksinimi olan durumlarda ise ** crossbeam-channel** tercih edilebilir.
16
+ Kanallarda sahiplik _ (Ownership)_ kurallarına uygunluk vardır. Veri gönderildiğinde, alıcı taraf bunu ** recv()** veya
17
+ ** iter() ** fonksiyonları ile alır. Sahiplik göndericiden devralınır. Asenkron ve senkron uyarlamaları yazmak mümkündür.
18
+ ** Tokio ** veya ** async-std** gibi asenkron çalışma ortamları için artık bir standart haline gelmiş olan ** tokio::sync::
19
+ mpsc ** küfesi kullanılır. Performans gereksinimi olan durumlarda ise ** crossbeam-channel** tercih edilebilir.
20
20
21
21
## Temel Kullanım
22
22
23
- Kanal kullanımını en basit haliyle aşağıdaki gibi ele alabiliriz .
23
+ Kanal kullanımını en basit haliyle aşağıdaki gibi ele alınabilir .
24
24
25
25
``` rust
26
26
use std :: sync :: mpsc :: channel;
@@ -43,13 +43,13 @@ pub fn hello_channels() {
43
43
}
44
44
```
45
45
46
- Bu örnekte spawn metodu ile açılan thread içerisinde message değişkeninin sahip olduğu veri ana thread'de receiver ile
47
- yakalanır ve ekrana basılır. channel çağrısı generic Sender ve Receiver veri yapılarının generic nesne örneklerini
48
- gönderir . Buna göre transmitter _ (yani Sender)_ nesnesini kullanarak bir thread içerisinde kanala mesaj gönderimi
49
- sağlanabilir. Bu örnekte String türünden bir içerik gönderilmektedir. Receiver nesne örneğinin recv fonksiyonu ile de
50
- kanala bırakılan mesaj yakalanmaktadır. recv fonksiyonu kanaldaki mesaj gelene kadar çalıştığı thread'i bekletir.
51
- Örnekte dikkat edilmesi gereken noktalardan birisi de message değişkenini kanala gönderdikten sonra yeniden kullanmaya
52
- çalışmaktır. Bu aşağıdaki hatanın oluşmasına sebebp olur.
46
+ Bu örnekte ** spawn** metodu ile açılan ** thread** içerisinde ** message** değişkeninin sahip olduğu veri ana thread'de
47
+ ** receiver ** ile yakalanır ve ekrana basılır. ** channel** çağrısı ** generic Sender** ve ** Receiver** veri yapılarının
48
+ generic nesne örneklerini döndürür . Buna göre transmitter _ (yani Sender)_ nesnesini kullanarak bir thread içerisinde
49
+ kanala mesaj gönderimi sağlanabilir. Bu örnekte ** String** türünden bir içerik gönderilmektedir. ** Receiver** nesne
50
+ örneğinin ** recv ** fonksiyonu ile de kanala bırakılan mesaj yakalanmaktadır. ** recv** fonksiyonu kanaldaki mesaj gelene
51
+ kadar çalıştığı thread'i bekletir. Örnekte dikkat edilmesi gereken noktalardan birisi de ** message** değişkenini kanala
52
+ gönderdikten sonra yeniden kullanmaya çalışmaktır. Bu kullanım aşağıdaki hatanın oluşmasına sebebp olur.
53
53
54
54
``` text
55
55
error[E0382]: borrow of moved value: `message`
@@ -68,11 +68,12 @@ error[E0382]: borrow of moved value: `message`
68
68
|
69
69
```
70
70
71
- Tabii bu durumda copy trait'i ile beslenen türler için söz konusu olmaz zira ilgili veriler kanala kopyalanarak taşınır.
71
+ Tabii bu durum ** copy** trait'i ile beslenen türler için söz konusu olmaz zira ilgili veriler kanala kopyalanarak
72
+ taşınır.
72
73
73
74
## Multi-Producer Kullanımı
74
75
75
- Aşağıdaki örnek kod parçasında ise birden fazla gönderici ele alınır .
76
+ Aşağıdaki örnek kod parçasında ise birden fazla gönderici ele alınmaktadır .
76
77
77
78
``` rust
78
79
use std :: sync :: mpsc :: channel;
@@ -106,15 +107,14 @@ pub fn multi_producer() {
106
107
}
107
108
```
108
109
109
- Bu örnekte 10 farklı ** thread** kanala mesaj bırakır. Thread'ler ** spawn** çağırısı ile ayağa kaldırılmadan önce *
110
- * transmitter** nesnesinin bir klonunun oluşturulduğunda dikkat edilmelidir. Her bir ** thread** kendi ** transmitter**
111
- klonunu kullanarak kanala mesaj bırakır. Mesajlar kanala senkron sırada bırakılır. İlerleyen satırlarda bir ** for**
112
- döngüsü ile kanala gelen mesajların ** Receiver** nesnesi üzerinden yakalanması işlemi gerçekleştirilir. Dikkat edilmesi
113
- gereken noktalardan birisi de ** drop** çağrısıdır. Açık bir şekilde ** transmitter** nesnesi açıkça ** drop** edilmiştir.
114
- Bu yapılmadığı durumda receiver dan mesajlar dinlenmeye devam eder ve program sonlanmaz. Zire klonlanan receiver
115
- örnekler halen daha yaşamaktadır. Bazı durumlarda zaten istenen bir durumdur. Sürekli dinlemede kalması gereken bir
116
- receiver gerektiren senaryolar buna örnek verilebilir. Farklı bir örnekle devam edip otomatik kapanma durumunu ele
117
- alalım.
110
+ Senaryoda birbirinden bağımsız çalışan 10 farklı ** thread** aynı kanala mesaj bırakır. Thread'ler ** spawn** çağırısı ile
111
+ ayağa kaldırılmadan önce ** transmitter** nesnesinin bir klonunun oluşturulduğunda dikkat edilmelidir. Her bir ** thread**
112
+ kendi ** transmitter** klonunu kullanarak kanala mesaj bırakır. Mesajlar kanala senkron sırada bırakılır. İlerleyen
113
+ satırlarda bir ** for** döngüsü ile kanala gelen mesajların ** Receiver** nesnesi aracılığıyla yakalanması sağlanır.
114
+ Dikkat edilmesi gereken noktalardan birisi de ** drop** çağrısıdır. Açık bir şekilde ** transmitter** nesnesi ** drop**
115
+ edilmiştir. Bu yapılmadığı takdirde ** receiver** ' dan mesajlar dinlenmeye devam eder ve program sonlanmaz. Zira
116
+ klonlanan receiver örnekleri halen daha aktiftir. Bazı senaryolarda bu zaten istenen bir durumdur. Farklı bir örnekle
117
+ devam edip otomatik kapanma durumunu ele alalım.
118
118
119
119
``` rust
120
120
use std :: sync :: mpsc :: channel;
@@ -148,16 +148,17 @@ pub fn multi_producer() {
148
148
}
149
149
```
150
150
151
- Bu örnekte transmitter ve transmitter_clone nesneleri tanımlandıkları thread'ler sonlandığında düşürülürler ve
152
- dolayısıyla receiver üzerinden yakalanacak kanal mesajlarının sayısı bellidir. Dolayısıyla program beklendiği şekilde
153
- tüm kanal mesajları işlendikten sonra sonlanır.
151
+ Bu örnekte ** transmitter** ve ** transmitter_clone** nesneleri tanımlandıkları iş parçacıkları sonlandığında bellekten
152
+ düşürülür ve dolayısıyla ** receiver** üzerinden yakalanacak kanal mesajlarının sayısı bellidir. Dolayısıyla program
153
+ beklendiği şekilde tüm kanal mesajları işlendikten sonra sonlanır.
154
154
155
155
## Örnek Senaryo
156
156
157
157
Kanal kullanımları ile ilgili örnek bir senaryo ele alalım. Bu senaryoda sistemdeki n sayıda rapor dosyasının n thread
158
- ile işlenmesi söz konusudur. Her bir thread ele aldığı dosyayı işledikten sonra kanal üzerine bilgi bırakır. En sonunda
159
- tüm bu bilgiler receiver üzerinden toplanır. İlk versiyonda standart kütüphanenin Sender ve Receiver yapıları
160
- kullanılmaktadır.
158
+ ile işlenmesi söz konusudur. Her bir ** thread** ele aldığı dosyayı işledikten sonra kanala bir bilgi bırakır. Bir gerçek
159
+ hayat senaryosunda işlem sonucu, raporun ayrı bir formata çevrilmesi, farklı ağlardaki servislere gönderilmesi,
160
+ parçalanarak efektif şekilde işlenmesi gibi süreçler işletilebilir. En sonunda tüm bu bilgiler receiver üzerinden
161
+ toplanır. İlk versiyonda standart kütüphanedeki ** Sender** ve ** Receiver** yapıları kullanılmaktadır.
161
162
162
163
``` rust
163
164
use std :: sync :: mpsc :: channel;
@@ -189,7 +190,7 @@ pub fn process_reports() {
189
190
. send (format! (" Processing '{}' report..." , report ))
190
191
. unwrap ();
191
192
192
- // Rapor dosyalarının işlendiği bazı business'lar çağırdığımızı düşünelim
193
+ // Rapor dosyalarının işlendiği bazı süreçlerin işletildiğini düşünelim
193
194
194
195
thread :: sleep (Duration :: from_secs (sleep_time ));
195
196
@@ -211,16 +212,17 @@ pub fn process_reports() {
211
212
}
212
213
```
213
214
214
- Her dosya sıralı bir şekilde döngüye girer ve herbirisi için ayrı bir ** thread** açılır. Bu thread'lerde dosyalar ile
215
+ Her dosya sıralı şekilde döngüye girer ve herbirisi için ayrı bir ** thread** açılır. Bu thread'lerde dosyalar ile
215
216
ilgili farklı iş süreçlerinin işletildiğini düşünebiliriz. Dosya işleyişlerinin farklı sürelerde gerçekleştiğini simüle
216
- etmek için rand kütüphanesi ile üretilen rastgele değerlerde beklemeler yapılmaktadır .
217
+ etmek için ** rand** küfesi ile üretilen rastgele sürelerde duraksatmalar yapılır .
217
218
218
219
## Asenkronluk
219
220
220
- Rust'ın MPSC modülü aslında gerçek anlamda bir asenkronluk sağlamaz. Bir başka deyişle Sender'dan mesajlar asenkron
221
- olarak ilerletilebilse de Receiver tarafı bunları senkron olarak işler. Tam bir asenkron çalışma sağlayabilmek için
222
- yardımcı küfelerden _ (crates)_ yararlanılabilir. Aşağıdaki ilk senaryoda ana thread'in bloklanmasına neden olan bir
223
- döngü kullanımı söz konusudur.
221
+ Rust'ın ** MPSC** modülü aslında gerçek anlamda bir asenkronluk sağlamaz. ** Sender** nesnesi üzerinden iletilen mesajlar
222
+ asenkron olarak yönlendirilse de ** Receiver** tarafı bunları senkron olarak işler. Tam bir asenkron çalışma
223
+ sağlayabilmek için yardımcı küfelerden _ (crates)_ yararlanılabilir. Aşağıdaki ilk senaryoda ana iş parçacığının
224
+ bloklanmasına neden olacak şekilde kasıtlı bir döngü kullanımı söz konusudur. Döngünün on defa işleyişini tamamlaması
225
+ gerekir.
224
226
225
227
``` rust
226
228
use std :: sync :: mpsc :: channel;
@@ -246,7 +248,7 @@ pub fn do_with_standard() {
246
248
247
249
println! (" Waiting for all tasks..." );
248
250
249
- // Aşağıdaki döngü main thread üzerine çalışıp buradaki akışı bloklamaya neden olacak
251
+ // Aşağıdaki döngü main thread içerisinde çalışıp akışı bloklamaya neden olur
250
252
for i in 0 .. 10 {
251
253
thread :: sleep (Duration :: from_secs (1 ));
252
254
println! (" Main task is working...Counting {}" , i );
@@ -262,9 +264,9 @@ pub fn do_with_standard() {
262
264
263
265
![ Runtime] ( Runtime.png )
264
266
265
- Aynı örneği ** tokio** küfesini kullanarak ele aldığımızda ise ana thread'in bloklanmadan for döngüsünün işletildiğini
266
- görebiliriz. Bu receiver tarafında mesajların asenkron ele alınabildiğinin de ispatıdır. tokio küfesini kullanmak için *
267
- * Full feature** seti ile eklenmesi gerekmektedir.
267
+ Aynı örneği ** tokio** küfesini kullanarak ele alalım. Bu sefer ana ** thread** 'i bloklayan for döngüsünü de asenkron bir
268
+ task olarak başlatalım. Buna göre receiver tarafının ilgili mesajları asenkron olarak yakalaması beklenir. ** tokio* *
269
+ küfesini kullanmak için projeye * * Full feature** seti ile eklenmesi gerekmektedir.
268
270
269
271
``` bash
270
272
cargo add tokio -F full
0 commit comments