GoDasturchi
The Resource That Never Closes

The Resource That Never Closes

Hech qachon yopilmagan resurs

Kutubxonadan kitob olayotganingizni tasavvur qiling: har safar kitob olganingizda, uni QAYTARISH kerak, shunda keyingi kishi ham o'sha kitobni ololadi. Agar siz o'qib bo'lgandan keyin kitobni HECH QACHON qaytarmasangiz, kutubxonaning javonlari sekin-asta BO'SHAB, oxir-oqibat hech kimga kitob yetmay qoladi. Fayl, tarmoq ulanishi yoki boshqa "resurs"ni ochib, hech qachon YOPMASLIK — aynan shunday: har bir ochilgan resurs, keyingisiga xalaqit beradi.

Quyidagi kod review'ga yuborilgan: "Berilgan fayldagi barcha qatorlarni o'qib, ro'yxat sifatida qaytaradi."

reader.go
func ReadAllLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("open file: %w", err)
	}

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("scan file: %w", err)
	}
	return lines, nil
}

Kod diqqat bilan yozilganga o'xshaydi: xato o'ralgan (%w bilan), scanner.Err() ham tekshirilgan — File I/O kursida ko'rgan yaxshi odatlar bor. Lekin bitta muhim qator YETISHMAYDI: file.Close() HECH QACHON chaqirilmaydi. os.Open orqali ochilgan fayl tizim resursi (fayl deskriptori)ni band qiladi, va bu resurs faqat Close() chaqirilganda BO'SHATILADI.

  • Bitta chaqiruvda bu sezilmaydi — operatsion tizim faylni dastur tugagach avtomatik yopadi
  • Lekin agar ReadAllLines uzoq ishlaydigan serverda, MINGLAB marta chaqirilsa (masalan, har bir HTTP so'rovda), har bir chaqiruv YANGI, yopilmagan fayl deskriptori qo'shadi — bu Case Study: The Memory That Grows Overnight darsidagi "kechasi bo'yicha o'sib boruvchi" muammoning boshqa turi
  • Oxir-oqibat operatsion tizimning "ochiq fayllar" chegarasiga yetib, dastur too many open files xatosi bilan ishdan chiqadi — buni birinchi marta ishga tushirganda hech qachon ko'rmaysiz, faqat KO'P marta chaqirilgandan keyin

To'g'ri kod — fayl muvaffaqiyatli ochilgandan DARHOL keyin, defer file.Close() qo'shish: file, err := os.Open(path); if err != nil { ... }; defer file.Close(). defer — File I/O kursida ko'rganingizdek, funksiya QANDAY tugashidan (muvaffaqiyatli yoki xato bilan) qat'iy nazar, Close() chaqirilishini KAFOLATLAYDI.

Review'da bu turdagi xatoni topish oson qoida bilan: har safar os.Open, net.Dial, sql.DB.Query, yoki boshqa "ochish/olish" nomi bilan boshlanadigan funksiya chaqirilganda, DARHOL yonida (yoki bir necha qator pastda) mos Close() yoki defer Close() BORMI, deb tekshiring. Yo'q bo'lsa — bu deyarli har doim resurs sizib chiqishi.

Key Takeaway

Key Takeaway:

Har bir ochilgan resurs (fayl, tarmoq ulanishi, baza kursori) uchun mos Close() chaqiruvi bormi, deb tekshiring — imkon qadar defer bilan. Bitta chaqiruvda bu xato sezilmaydi, lekin ko'p marta takrorlanganda resurslar sekin-asta tugab, dastur ishdan chiqadi.

NEXT UP

Off by One at the Boundary

The Resource That Never Closes

Hech qachon yopilmagan resurs

Kutubxonadan kitob olayotganingizni tasavvur qiling: har safar kitob olganingizda, uni QAYTARISH kerak, shunda keyingi kishi ham o'sha kitobni ololadi. Agar siz o'qib bo'lgandan keyin kitobni HECH QACHON qaytarmasangiz, kutubxonaning javonlari sekin-asta BO'SHAB, oxir-oqibat hech kimga kitob yetmay qoladi. Fayl, tarmoq ulanishi yoki boshqa "resurs"ni ochib, hech qachon YOPMASLIK — aynan shunday: har bir ochilgan resurs, keyingisiga xalaqit beradi.

Quyidagi kod review'ga yuborilgan: "Berilgan fayldagi barcha qatorlarni o'qib, ro'yxat sifatida qaytaradi."

reader.go
func ReadAllLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("open file: %w", err)
	}

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("scan file: %w", err)
	}
	return lines, nil
}

Kod diqqat bilan yozilganga o'xshaydi: xato o'ralgan (%w bilan), scanner.Err() ham tekshirilgan — File I/O kursida ko'rgan yaxshi odatlar bor. Lekin bitta muhim qator YETISHMAYDI: file.Close() HECH QACHON chaqirilmaydi. os.Open orqali ochilgan fayl tizim resursi (fayl deskriptori)ni band qiladi, va bu resurs faqat Close() chaqirilganda BO'SHATILADI.

  • Bitta chaqiruvda bu sezilmaydi — operatsion tizim faylni dastur tugagach avtomatik yopadi
  • Lekin agar ReadAllLines uzoq ishlaydigan serverda, MINGLAB marta chaqirilsa (masalan, har bir HTTP so'rovda), har bir chaqiruv YANGI, yopilmagan fayl deskriptori qo'shadi — bu Case Study: The Memory That Grows Overnight darsidagi "kechasi bo'yicha o'sib boruvchi" muammoning boshqa turi
  • Oxir-oqibat operatsion tizimning "ochiq fayllar" chegarasiga yetib, dastur too many open files xatosi bilan ishdan chiqadi — buni birinchi marta ishga tushirganda hech qachon ko'rmaysiz, faqat KO'P marta chaqirilgandan keyin

To'g'ri kod — fayl muvaffaqiyatli ochilgandan DARHOL keyin, defer file.Close() qo'shish: file, err := os.Open(path); if err != nil { ... }; defer file.Close(). defer — File I/O kursida ko'rganingizdek, funksiya QANDAY tugashidan (muvaffaqiyatli yoki xato bilan) qat'iy nazar, Close() chaqirilishini KAFOLATLAYDI.

Review'da bu turdagi xatoni topish oson qoida bilan: har safar os.Open, net.Dial, sql.DB.Query, yoki boshqa "ochish/olish" nomi bilan boshlanadigan funksiya chaqirilganda, DARHOL yonida (yoki bir necha qator pastda) mos Close() yoki defer Close() BORMI, deb tekshiring. Yo'q bo'lsa — bu deyarli har doim resurs sizib chiqishi.

Key Takeaway

Key Takeaway:

Har bir ochilgan resurs (fayl, tarmoq ulanishi, baza kursori) uchun mos Close() chaqiruvi bormi, deb tekshiring — imkon qadar defer bilan. Bitta chaqiruvda bu xato sezilmaydi, lekin ko'p marta takrorlanganda resurslar sekin-asta tugab, dastur ishdan chiqadi.

NEXT UP

Off by One at the Boundary