Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete ch16-03-"shared-state" by Saidasror.md #61

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 86 additions & 121 deletions rustbook-uz/src/ch16-03-shared-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,130 +38,105 @@ Qulfni ushlab turgan boshqa thread panic qo'zg'atsa, `lock` chaqiruvi muvaffaqiy

Qulfni qo'lga kiritganimizdan so'ng, biz bu holatda `num` deb nomlangan return qiymatini ichidagi ma'lumotlarga o'zgaruvchan reference sifatida ko'rib chiqishimiz mumkin. Tur(type) tizimi `m` dagi qiymatni ishlatishdan oldin qulfni olishimizni ta'minlaydi. `m` turi `i32` emas, `Mutex<i32>`, shuning uchun biz `i32` qiymatidan foydalanish uchun `lock`ni chaqirishimiz kerak. Biz unuta olmaymiz; aks holda turdagi tizim bizga ichki `i32` ga kirishga ruxsat bermaydi.

As you might suspect, `Mutex<T>` is a smart pointer. More accurately, the call
to `lock` *returns* a smart pointer called `MutexGuard`, wrapped in a
`LockResult` that we handled with the call to `unwrap`. The `MutexGuard` smart
pointer implements `Deref` to point at our inner data; the smart pointer also
has a `Drop` implementation that releases the lock automatically when a
`MutexGuard` goes out of scope, which happens at the end of the inner scope. As
a result, we don’t risk forgetting to release the lock and blocking the mutex
from being used by other threads, because the lock release happens
automatically.

After dropping the lock, we can print the mutex value and see that we were able
to change the inner `i32` to 6.

#### Sharing a `Mutex<T>` Between Multiple Threads

Now, let’s try to share a value between multiple threads using `Mutex<T>`.
We’ll spin up 10 threads and have them each increment a counter value by 1, so
the counter goes from 0 to 10. The next example in Listing 16-13 will have
a compiler error, and we’ll use that error to learn more about using
`Mutex<T>` and how Rust helps us use it correctly.
Taxmin qilgan bolishingiz mumkinki Mutex<T> aqlli ko'rsatgich. Aniqroq qilib aytadigan bo'lsak, `lock` qo'ng'irog'i MutexGuard deb nomlangan ochish
qo'ng'irog'i bilan oralgan LockResult-ga o'ralgan aqlli ko'rsatgichni qaytaradi . `MutexGuard` ko'rsatkichi esa bizning ichki ma'lumotlarimizga ishora
qilish uchun `Deref`ni amalga oshiradi( Derefdan foydalanadi). Aqlli ko'rsatgichda `Drop` ilovasi ham mavjud bo'lib, MutexGuard qo'llanilish doirasidan
tashqariga chiqqanda avtomatik ravishda qulfni chiqaradi va bu esa ichki doiraning oxirida sodir bo'ladi. Natijada, biz qulfni(lock) bo'shatishni unutib
qo'ymaymiz va asosiysi mutexni boshqa threadlar tomonidan ishlatilishini bloklaymiz, chunki qulfni(lock) chiqarish avtomatik ravishda sodir bo'ladi.

Qulfni tashlaganimizdan so'ng, biz mutex qiymatini print qilishimiz(chop etishimiz ) va ichki `i32` ni 6 ga o'zgartira olganimizni ko'rishimiz mumkin.

#### Bitta `Muteks<T>`ni Bir nechta mavzular o'rtasida ulashish(almashtirish):

Keling, `Mutex<T>`-dan foydalanib, bir nechta oqimlar o'rtasida qiymatni share qilishga(qiymatni almashtirishga) harakat qilaylik. Biz 10 ta threadni
aylantiramiz va ularning har biri hisoblagich qiymatini 1 ga oshiradi, shuning uchun hisoblagich 0 dan 10 gacha boradi. 16-13 ro'yxatdagi keyingi misolda
kompilyator xatosi (compiler error)bo'ladi va biz bu xatoni o'rganish uchun ishlatamiz. `Mutex<T>`-dan foydalanish va Rust uni to'g'ri ishlatishimizga
qanday yordam berishi haqida ko'proq o'rganamiz.

<span class="filename">Fayl nomi: src/main.rs</span>

```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-13/src/main.rs}}
```

<span class="caption">Listing 16-13: Ten threads each increment a counter
guarded by a `Mutex<T>`</span>
<span class="caption">Ro'yxat 16-13: `Mutex<T>` tomonidan qo'riqlanadigan hisoblagichni har biri o'nta threaddan amalga oshirishi.</span>

We create a `counter` variable to hold an `i32` inside a `Mutex<T>`, as we did
in Listing 16-12. Next, we create 10 threads by iterating over a range of
numbers. We use `thread::spawn` and give all the threads the same closure: one
that moves the counter into the thread, acquires a lock on the `Mutex<T>` by
calling the `lock` method, and then adds 1 to the value in the mutex. When a
thread finishes running its closure, `num` will go out of scope and release the
lock so another thread can acquire it.
`Mutex<T>`ni ichida `i32` ni ushlab turish uchun hisoblagich o'zgaruvchisini yaratamiz, xuddi 16-12 ro'yxatdagi kabi(listing 16-12). Keyingi amal esa,
biz raqamlar oralig'ida takrorlash orqali 10 ta thread yaratamiz. Biz `thread::spawn` dan foydalanamiz va barcha threadlarga bir xil yopilishni beramiz:
hisoblagichni threadga o'tkazish uchun ishlatiladigan qulflash usulini amalga oshirish orqali(chaqirish orqali) orqali `Mutex<T>` da blokirovkaga ega
bo'ladi va keyin mutexdagi qiymatga 1 qo'shiladi. Thread o'zining yopilishini tugatgandan so'ng, `num` doirasi tashqariga chiqadi va boshqa thread uni
olishi uchun qulfni bo'shatadi.

In the main thread, we collect all the join handles. Then, as we did in Listing
16-2, we call `join` on each handle to make sure all the threads finish. At
that point, the main thread will acquire the lock and print the result of this
program.
Asosiy threadda biz barcha birlashma tutqichlarini yig'amiz. Keyin, 16-2 ro'yxatdagidek,barcha threadlar tugashiga ishonch hosil qilish uchun har bir
tutqichga `join` chaqiramiz. O'sha paytda asosiy thread qulfni oladi va ushbu dasturning natijasini print(chop etadi).

We hinted that this example wouldn’t compile. Now let’s find out why!
Bu misol tuzilmasligiga ishora qilingan. Endi nima uchunligini o'ylab ko'raylik!

```console
{{#include ../listings/ch16-fearless-concurrency/listing-16-13/output.txt}}
```

The error message states that the `counter` value was moved in the previous
iteration of the loop. Rust is telling us that we can’t move the ownership
of lock `counter` into multiple threads. Let’s fix the compiler error with a
multiple-ownership method we discussed in Chapter 15.
Xato xabari `counter`(hisoblagich) qiymati tsiklning oldingi iteratsiyasida ko'chirilganligini bildiradi. Rust bizga qulflash `counter`(hisoblagichining)
egaligini bir nechta mavzularga o'tkaza olmasligimizni aytadi. Keling, 15-bobda muhokama qilgan bir nechta egalik usuli bilan kompilyator xatosini
tuzataylik.

#### Multiple Ownership with Multiple Threads
#### Bir nechta mavzular bilan bir nechta egalik

In Chapter 15, we gave a value multiple owners by using the smart pointer
`Rc<T>` to create a reference counted value. Let’s do the same here and see
what happens. We’ll wrap the `Mutex<T>` in `Rc<T>` in Listing 16-14 and clone
the `Rc<T>` before moving ownership to the thread.
15-bobda mos yozuvlar hisoblangan qiymatni yaratish uchun aqlli ko'rsatkich Rc<T> yordamida bir nechta egalarga qiymat berdik. Bu yerda ham xuddi shunday
qilaylik va nima bo'lishini ko'ramiz. `Mutex<T>`-ni `Rc <T>`-ga 16-14-listingda o'rab olamiz va egalikni threadga ko'chirishdan oldin `Rc<T>`-ni
klonlaymiz(nusxasini yaratmoq, cloning).

<span class="filename">Fayl nomi: src/main.rs</span>

```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-14/src/main.rs}}
```

<span class="caption">Listing 16-14: Attempting to use `Rc<T>` to allow
multiple threads to own the `Mutex<T>`</span>
<span class="caption">Listing 16-14: `Rc<T>` ni ishlatib, bir nechta iplar (threads) `Mutex<T>` ga egalik qilishiga imkon berishga urinish.</span>

Once again, we compile and get... different errors! The compiler is teaching us
a lot.
Yana bir bor, biz kompilyatsiya qilamiz va... turli xatolarni olamiz! Kompilyator bizga ko'p narsani o'rgatmoqda.

```console
{{#include ../listings/ch16-fearless-concurrency/listing-16-14/output.txt}}
```

Wow, that error message is very wordy! Here’s the important part to focus on:
`` `Rc<Mutex<i32>>` cannot be sent between threads safely ``. The compiler is
also telling us the reason why: ``the trait `Send` is not implemented for
`Rc<Mutex<i32>>` ``. We’ll talk about `Send` in the next section: it’s one of
the traits that ensures the types we use with threads are meant for use in
concurrent situations.

Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>`
manages the reference count, it adds to the count for each call to `clone` and
subtracts from the count when each clone is dropped. But it doesn’t use any
concurrency primitives to make sure that changes to the count can’t be
interrupted by another thread. This could lead to wrong counts—subtle bugs that
could in turn lead to memory leaks or a value being dropped before we’re done
with it. What we need is a type exactly like `Rc<T>` but one that makes changes
to the reference count in a thread-safe way.

#### Atomic Reference Counting with `Arc<T>`

Fortunately, `Arc<T>` *is* a type like `Rc<T>` that is safe to use in
concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically
reference counted* type. Atomics are an additional kind of concurrency
primitive that we won’t cover in detail here: see the standard library
documentation for [`std::sync::atomic`][atomic]<!-- ignore --> for more
details. At this point, you just need to know that atomics work like primitive
types but are safe to share across threads.

You might then wonder why all primitive types aren’t atomic and why standard
library types aren’t implemented to use `Arc<T>` by default. The reason is that
thread safety comes with a performance penalty that you only want to pay when
you really need to. If you’re just performing operations on values within a
single thread, your code can run faster if it doesn’t have to enforce the
guarantees atomics provide.

Let’s return to our example: `Arc<T>` and `Rc<T>` have the same API, so we fix
our program by changing the `use` line, the call to `new`, and the call to
`clone`. The code in Listing 16-15 will finally compile and run:
Afsus, bu xato xabari juda uzun va yoqimsizroq(rasmiyligi uchun) ekan! Bu yerda diqqat qilish kerak bo'lgan muhim qism:
`` Rc<Mutex<i32>> ` threadlar o'rtasida xavfsiz yuborilishi mumkin emas ``. Kompilyator bizga buning sababini ham aytib beradi: `Send ` xususiyati
`Rc<Mutex<i32>> ` uchun amalga oshirilmagan. Keyingi bo'limda` Send`(Yuborish) haqida gaplashamiz: bu biz threadlar bilan ishlatadigan turlarni bir
vaqtda vaziyatlarda foydalanishga mo'ljallanganligini ta'minlaydigan xususiyatlardan biridir.

Afsuski, `Rc<T>` ni threadlar bo'ylab almashish xavfsiz emas(yuqorida ham aytilishicha mumkin ham emas). `Rc<T>` mos yozuvlar sonini boshqarganda, u clone
(klonlash) uchun har bir qo'ng'iroq uchun hisobni qo'shadi va har bir clone(klon) tushirilganda hisobdan ayiradi. Ammo hisobdagi o'zgarishlarni boshqa
oqim bilan to'xtatib qo'ymasligiga ishonch hosil qilish uchun u parallellik ibtidoiylaridan(parallallik ibtidoiysi bu concurrency yani raqobatga tegishli
mavzu) foydalanmaydi. Bu noto'g'ri hisob-kitoblarga olib kelishi mumkin - nozik xatolar, o'z navbatida, xotiraning oqishi yoki biz bilan ishlash
tugashidan oldin qiymatning tushib ketishiga olib kelishi mumkin. Bizga aynan `Rc<T>`ga o'xshash tur kerak bo'ladi, ammo u mos yozuvlar soniga
o'zgartirish kiritadi.

#### `Arc<T>` bilan atomik havolalarni hisoblash

Yaxshiyamki, `Arc<T>` `Rc<T>` kabi bir xil vaziyatlarda foydalanish uchun xavfsiz tur. A atomik degan ma'noni anglatadi, ya'ni bu atomik havola orqali
hisoblangan tur. Atomlar parallellik ibtidoiyning(concurrency:konkurentlik) qo'shimcha turi bo'lib,bu yerda batafsik ko'rib chiqolmaymiz: batafsil
ma'lumot uchun `std::sync::atomic` uchun standart kutubxona hujjatlariga(dokumentatsiyasiga) qarang. Shu nuqtada, atomlar ibtidoiy turlar kabi ishlashini
bilishingiz kerak, lekin ularni threadlar bo'ylab almashish xavfsizdir.

Keyin nima uchun barcha ibtidoiy turlar atom emasligi va nega standart kutubxona turlari sukut bo'yicha `Arc<T>` dan foydalanish uchun amalga
oshirilmaganligi haqida hayron bo'lishingiz mumkin. Buning sababi shundaki, thread xavfsizligi faqat sizga kerak bo'lganda to'lamoqchi bo'lgan ishlash
jazosi bilan birga keladi(PERFORMANCE PENALTY-IJRO,BAJARISH UCHUN JAZO) .Agar siz faqat bitta oqim ichidagi qiymatlar ustida amllarni bajarayotgan
bo'lsangiz yani atomik kafolatlarni bajarish shart bo'lmasa, kodingiz tezroq ishlashi mumkin.

Keling, misolimizga qaytaylik: `Arc<T>` va `Rc<T>` bir xil APIga ega, shuning uchun biz dasturimizni `use`(foydalanish) qatorini, `new`(yangi) chaqiruvni
va `clone`(klonlash) uchun qo'ng'iroqni o'zgartirish orqali tuzatamiz. 16-15 ro'yxatdagi kod nihoyat togri boladi:

<span class="filename">Fayl nomi: src/main.rs</span>

```rust
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-15/src/main.rs}}
```

<span class="caption">Listing 16-15: Using an `Arc<T>` to wrap the `Mutex<T>`
to be able to share ownership across multiple threads</span>
<span class="caption">Ro'yxat 16-15: `Mutex<T>`-ni o'rash uchun `Arc<T>` dan foydalanish, bir nechta mavzular bo'ylab egalik huquqini baham ko'rish
uchun</span>

This code will print the following:
Ushbu kod quyidagilarni print qiladi:

<!-- Not extracting output because changes to this output aren't significant;
the changes are likely to be due to the threads running differently rather than
Expand All @@ -171,40 +146,30 @@ changes in the compiler -->
Result: 10
```

We did it! We counted from 0 to 10, which may not seem very impressive, but it
did teach us a lot about `Mutex<T>` and thread safety. You could also use this
program’s structure to do more complicated operations than just incrementing a
counter. Using this strategy, you can divide a calculation into independent
parts, split those parts across threads, and then use a `Mutex<T>` to have each
thread update the final result with its part.

Note that if you are doing simple numerical operations, there are types simpler
than `Mutex<T>` types provided by the [`std::sync::atomic` module of the
standard library][atomic]<!-- ignore -->. These types provide safe, concurrent,
atomic access to primitive types. We chose to use `Mutex<T>` with a primitive
type for this example so we could concentrate on how `Mutex<T>` works.

### Similarities Between `RefCell<T>`/`Rc<T>` and `Mutex<T>`/`Arc<T>`

You might have noticed that `counter` is immutable but we could get a mutable
reference to the value inside it; this means `Mutex<T>` provides interior
mutability, as the `Cell` family does. In the same way we used `RefCell<T>` in
Chapter 15 to allow us to mutate contents inside an `Rc<T>`, we use `Mutex<T>`
to mutate contents inside an `Arc<T>`.

Another detail to note is that Rust can’t protect you from all kinds of logic
errors when you use `Mutex<T>`. Recall in Chapter 15 that using `Rc<T>` came
with the risk of creating reference cycles, where two `Rc<T>` values refer to
each other, causing memory leaks. Similarly, `Mutex<T>` comes with the risk of
creating *deadlocks*. These occur when an operation needs to lock two resources
and two threads have each acquired one of the locks, causing them to wait for
each other forever. If you’re interested in deadlocks, try creating a Rust
program that has a deadlock; then research deadlock mitigation strategies for
mutexes in any language and have a go at implementing them in Rust. The
standard library API documentation for `Mutex<T>` and `MutexGuard` offers
useful information.

We’ll round out this chapter by talking about the `Send` and `Sync` traits and
how we can use them with custom types.
Biz uddaladik! Biz 0 dan 10 gacha hisobladik, bu katta ishdek korinmasligi mumkin, ammo bu bizga `Mutex<T>` va thread xavfsizligi haqida ko‘p narsalarni
o‘rgatadi. Hisoblagich faqat ko'paytirishdan koproq ish qila olishini orgatdi. Ushbu strategiyadan foydalanib, siz hisobni mustaqil qismlarga
bo'lishingiz, bu qismlarni threadlar bo'ylab ajratishingiz va keyin `Mutex<T>` dan foydalanib, har bir thread yakuniy natijani o'z qismi bilan yangilashi
mumkin.

E'tibor bering, agar siz oddiy raqamli amallarni bajarayotgan bo'lsangiz, standart kutubxonaning `std::sync::atomic` modulida taqdim etilgan `Mutex<T>`
turlaridan oddiyroq turlar mavjud. Ushbu turlar ibtidoiy turlarga xavfsiz, parallel, atomik kirishni ta'minlaydi va ushbu misol uchun `Mutex<T>`ning
ibtidoiy turi bilan foydalanishni tanladik, shuning uchun `Mutex<T>` qanday ishlashiga e'tibor qaratishimiz mumkin.

### `RefCell<T>`/`Rc<T>` va `Mutex<T>`/`Arc<T>` o'rtasidagi o'xshashliklar

Hisoblagich(counter) o'zgarmasligini payqagan bo'lishingiz mumkin, lekin biz uning ichidagi qiymatga o'zgaruvchan havolani olishimiz mumkin; bu `Mutex<T>
Cell` oilasi kabi ichki o'zgaruvchanlikni qollab quvvatlaydi. Xuddi shu tarzda biz `Rc<T>` ichidagi tarkibni o'zgartirishga ruxsat berish uchun 15-bobda
`RefCell<T>` dan foydalanganmiz, `Arc<T>` ichidagi tarkibni mutatsiya qilish uchun `Mutex<T>` dan foydalanamiz.

Yana bir muhim ma' lumot, `Mutex<T>` dan foydalanganda Rust sizni barcha turdagi mantiqiy xatolardan himoya qila olmaydi. 15-bobda `Rc<T>` dan
foydalanish oziga xos yozuvlar sikllarini yaratish xavfi bilan kelganligini eslang, bu erda ikkita `Rc<T>` qiymati bir-biriga tegishli bo'lib, xotira
susayishi, tanqisligiga olib keladi. Xuddi shunday, `Mutex<T>` ham boshi berk deadlocks(ko'chalarni) yaratish xavfi bilan birga keladi. Bular amal ikkita
resursni bloklashi kerak bo'lganda sodir bo'ladi va ikkita thread har biri locks(qulflardan) birini qo'lga kiritib. Agar siz ziddiyatlarga qiziqsangiz,
tanqislik deadlocks(ko'chasiga) ega Rust dasturini yaratishga harakat qiling; keyin har qanday tilda mutekslar uchun ziddiyatni yengilashtirish, yechim
topish strategiyalarini o'rganing va Rustda ularni amalga oshirishga kirishing. `Mutex<T>` va `MutexGuard` uchun standart kutubxona API hujjatlari
foydali ma'lumotlarni taqdim etadi.

Biz ushbu bobni `Send`(Yuborish) va `Sync`(Sinxronlashtirish) xususiyatlari va ularni maxsus turlar bilan qanday ishlatishimiz haqida gapirib,
yakunlaymiz.

[atomic]: ../std/sync/atomic/index.html