- Published on
Swiftda Actor
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Actor β data race himoyasi
Ko'p vazifa bir xil ma'lumotni bir vaqtda o'zgartirganda β data race paydo bo'ladi. Bu dasturda kutilmagan xatolar, noto'g'ri natijalar va crash larga olib keladi. Actor bu muammoni hal qiladi β u reference type bo'lib (class ga o'xshash), lekin o'z ma'lumotlariga bir vaqtda faqat bitta vazifa kirishi mumkin. Boshqa vazifalar navbatda kutadi.
Actor β Swift 5.5 da kiritilgan yangi tur. U class kabi ishlaydi, lekin o'z property va metodlarini izolyatsiya qiladi. Tashqaridan actor ning property yoki metodiga murojaat qilganda await yozish kerak β bu navbatda kutish degani.
Data Race muammosi
Quyida ikkita misol bor. Birinchisida oddiy class ishlatiladi β 1000 ta parallel vazifa bir xil property ni o'zgartiradi va natija har safar boshqacha bo'ladi (data race). Ikkinchisida actor ishlatiladi β natija har doim to'g'ri.
// βββββββββββββββββββββββββββββββββββββββ
// β DATA RACE β class bilan
// βββββββββββββββββββββββββββββββββββββββ
class XavfliHisoblagich {
var son = 0
}
let hisoblagich = XavfliHisoblagich()
// 1000 ta parallel vazifa β bir xil property ni o'zgartiradi
for _ in 0..<1000 {
Task {
hisoblagich.son += 1 // β DATA RACE!
// Ikki task bir vaqtda son = 500 ni o'qiydi
// Ikkalasi ham son = 501 yozadi
// Bitta oshirish yo'qoldi!
}
}
// Natija: 1000 emas, har safar boshqacha son!
// Bu kutilmagan xatolar va crashlarga olib keladi
// βββββββββββββββββββββββββββββββββββββββ
// β
ACTOR BILAN β data race yo'q
// βββββββββββββββββββββββββββββββββββββββ
actor XavfsizHisoblagich {
var son = 0
// Actor ichida β sinxron kirish
func oshir() {
son += 1 // β
Xavfsiz β faqat bitta vazifa kiradi
}
func qiymat() -> Int {
return son
}
}
let xavfsiz = XavfsizHisoblagich()
for _ in 0..<1000 {
Task {
await xavfsiz.oshir() // await β navbat kutadi
}
}
// Natija: har doim 1000 β
Actor ishlash printsipi
Actor ning ichidagi funksiyalar izolyatsiya ichida ishlaydi. Bir vaqtda faqat bitta vazifa actor ning ichiga kira oladi β qolganlari navbatda kutadi. Actor dan tashqarida esa barcha chaqiruvlar await bilan amalga oshiriladi. let bilan e'lon qilingan property larga esa await siz ham kirish mumkin β chunki ular o'zgarmaydi va xavfsiz.
actor BankHisobi {
let egasi: String
private(set) var balans: Double
init(egasi: String, balans: Double) {
self.egasi = egasi
self.balans = balans
}
// Actor ichidagi funksiyalar β izolyatsiya ichida
// Bir vaqtda faqat bitta vazifa chaqira oladi
func kiritish(summa: Double) {
balans += summa
print("\(egasi): +\(summa), balans: \(balans)")
}
func yechish(summa: Double) -> Bool {
guard balans >= summa else {
print("\(egasi): mablag' yetarli emas")
return false
}
balans -= summa
print("\(egasi): -\(summa), balans: \(balans)")
return true
}
}
// Tashqaridan β await bilan
let hisob = BankHisobi(egasi: "Ali", balans: 1000)
Task {
await hisob.kiritish(summa: 500) // await kerak!
let balans = await hisob.balans // property ham await
let natija = await hisob.yechish(summa: 200)
}
// Actor diagrammasi:
// Task 1 ββββββΊβ β
// β ACTOR β β faqat bitta kiradi
// Task 2 βwaitββΊβ (navbat)β
// β β
// Task 3 βwaitββΊβ β
nonisolated β izolyatsiyadan chiqarish
Ba'zan actor ichidagi metod yoki property ni izolyatsiyadan chiqarish kerak bo'ladi. Masalan, faqat let property larni ishlatadigan metod uchun izolyatsiya ortiqcha. nonisolated kalit so'zi shu maqsadda ishlatiladi β bunday metod tashqaridan await siz chaqirilishi mumkin. Lekin nonisolated metod actor ichidagi var property larga kira olmaydi.
actor Foydalanuvchi {
let id: UUID // let β o'zgarmaydi, izolyatsiya kerak emas
let ism: String // let β o'zgarmaydi
var yosh: Int // var β izolyatsiya kerak
init(id: UUID, ism: String, yosh: Int) {
self.id = id
self.ism = ism
self.yosh = yosh
}
// nonisolated β bu funksiya izolyatsiya tashqarida
// Faqat let property larga kirishi mumkin
// Tashqaridan await siz chaqirish mumkin
nonisolated func tavsif() -> String {
return "\(ism) (ID: \(id))"
// yosh ga kira olmaydi β var bo'lgani uchun
}
// Oddiy metod β izolyatsiya ichida
func tugKunniNishonla() {
yosh += 1 // var β izolyatsiya kerak
}
}
let user = Foydalanuvchi(id: UUID(), ism: "Ali", yosh: 25)
// nonisolated β await kerak emas
let tavsif = user.tavsif() // β
sinxron
// Oddiy property/metod β await kerak
let yosh = await user.yosh // await kerak
await user.tugKunniNishonla()
Actor va Protocol
Actor ham protocol ga mos kelishi mumkin. Lekin protokol metodlari actor izolyatsiyasiga moslashishi kerak β shuning uchun protokoldagi metodlar async deb belgilanishi kerak.
// Actor protokolga mos kelishi mumkin
protocol Saqlash {
func saqlash(_ matn: String) async
func oqish() async -> String?
}
actor FaylSaqlash: Saqlash {
private var mazmun: String?
func saqlash(_ matn: String) {
mazmun = matn
}
func oqish() -> String? {
return mazmun
}
}
π― Topshiriq
actor Kesh yarating: private var saqlangan: [String: String]. saqlash(kalit:qiymat:) va olish(kalit:) metodlari bo'lsin. 100 ta parallel Task dan bir xil kalitga yozish/o'qish qiling. Actor tufayli data race bo'lmasligini ko'ring.