File Ruby. Terbuka dan kebutuhan untuk f.close

92

Sudah menjadi pengetahuan umum di sebagian besar bahasa pemrograman bahwa alur untuk bekerja dengan file bersifat buka-pakai-tutup. Namun saya melihat berkali-kali dalam kode ruby ​​panggilan File.open tak tertandingi, dan terlebih lagi saya menemukan permata pengetahuan ini di dokumen ruby:

Aliran I / O secara otomatis ditutup saat diklaim oleh pengumpul sampah.

darkredandyellow friendly irc menangani masalah ini:
[17:12] ya, dan juga, jumlah deskriptor file biasanya dibatasi oleh OS
[17:29] Saya berasumsi bahwa Anda dapat dengan mudah kehabisan deskriptor file yang tersedia sebelum pengumpul sampah membersihkan naik. dalam hal ini, Anda mungkin ingin menggunakan close mereka sendiri. "diklaim oleh pemulung." artinya GC bertindak di beberapa titik di masa mendatang. dan itu mahal. banyak alasan untuk menutup file secara eksplisit.

  1. Apakah kita perlu menutup secara eksplisit
  2. Jika ya, mengapa GC menutup otomatis?
  3. Jika tidak, lalu mengapa harus dipilih?
clyfe
sumber
1
'Pengetahuan umum' Anda sudah ketinggalan zaman sejak destruktor ditemukan.
meagar
1
@ Meager: Kapan destruktor ditemukan?
Andrew Grimm
Sekadar catatan: Meskipun deskriptor file terbatas, setidaknya di Linux, batasnya cukup tinggi.
Linuxios
1
@ Linuxios: di ubuntu12.04 saya $ ulimit -n => 1024itu hanya tinggi ketika Anda melakukan hanya beberapa pekerjaan sederhana. Kebiasaan buruk suatu saat akan menimbulkan masalah besar!
HVNSweeting

Jawaban:

133

Saya melihat berkali-kali dalam kode ruby File.openpanggilan tak tertandingi

Bisakah Anda memberi contoh? Saya hanya pernah melihat bahwa dalam kode yang ditulis oleh pemula yang tidak memiliki "pengetahuan umum di sebagian besar bahasa pemrograman bahwa aliran untuk bekerja dengan file adalah buka-pakai-tutup".

Rubyist berpengalaman menutup file mereka secara eksplisit, atau lebih idiomatis, menggunakan bentuk blok File.open, yang secara otomatis menutup file untuk Anda. Implementasinya pada dasarnya terlihat seperti ini:

def File.open(*args, &block)
  return open_with_block(*args, &block) if block_given?
  open_without_block(*args)
end

def File.open_without_block(*args)
  # do whatever ...
end

def File.open_with_block(*args)
  yield f = open_without_block(*args)
ensure
  f.close
end

Naskah adalah kasus khusus. Skrip umumnya berjalan sangat pendek, dan menggunakan begitu sedikit deskriptor file sehingga tidak masuk akal untuk menutupnya, karena sistem operasi akan tetap menutupnya saat skrip keluar.

Apakah kita perlu menutupnya secara eksplisit?

Iya.

Jika ya, mengapa GC menutup otomatis?

Karena setelah objek terkumpul, tidak ada cara bagi Anda untuk menutup file lagi, dan dengan demikian Anda akan membocorkan deskriptor file.

Perhatikan bahwa bukan pengumpul sampah yang menutup file. Pengumpul sampah hanya menjalankan finalizer apa pun untuk suatu objek sebelum mengumpulkannya. Kebetulan Filekelas mendefinisikan finalizer yang menutup file.

Jika tidak, lalu mengapa harus dipilih?

Karena memori yang terbuang itu murah, tetapi deskriptor file yang terbuang tidak. Oleh karena itu, tidak masuk akal untuk mengikat masa pakai deskriptor file ke masa pakai beberapa bagian memori.

Anda tidak bisa memprediksi kapan pengumpul sampah akan berjalan. Anda bahkan tidak dapat memprediksi apakah itu akan berjalan sama sekali : jika Anda tidak pernah kehabisan memori, pengumpul sampah tidak akan pernah berjalan, oleh karena itu finalizer tidak akan pernah berjalan, oleh karena itu file tidak akan pernah ditutup.

Jörg W Mittag
sumber
1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (meskipun Kernel # terbuka dan digunakan terutama untuk sisi HTTP-nya, tetapi saya mencapainya dengan parameter jalur file lokal. ..; Saya masih mencoba mencari waktu untuk menambal & meminta-tarik), github.com/jnicklas/carrierwave Ctrl + f "File.open" (ini diberikan sebagai contoh, tetapi dengan cara yang buruk ...), dan beberapa tempat lain yang tidak saya ingat. Saya memiliki masalah dengan masalah karena persyaratan stabilitas dalam proyek saya ..
clyfe
3
Dalam contoh ini, haruskah mengangkat berada di dalam blok penyelamatan? Bukankah ini hanya akan memunculkan kesalahan runtime jika kenaikan dipanggil dan tidak ada pengecualian?
Jeff Storey
@JeffStorey: tangkapan bagus! 17 bulan tanpa disadari…
Jörg W Mittag
@ JörgWMittag dan sekarang 17 bulan lagi belum diperbaiki: Saya kira poin utama di sini adalah ensure, rescuedan raisetidak diperlukan sama sekali.
KL-7
Saya pikir Anda tidak bisa ensuretanpa rescue. Dan Anda tidak bisa diam-diam menelan pengecualian, Anda harus menyebarkannya ke pemanggil, setelah menutup file. Bagaimanapun, ingatkan saya lagi di Mei '15 :-D
Jörg W Mittag
72

Anda harus selalu menutup deskriptor file setelah digunakan, itu juga akan menghapusnya. Seringkali orang menggunakan File.open atau metode yang setara dengan blok untuk menangani seumur hidup deskriptor file. Sebagai contoh:

File.open('foo', 'w') do |f|
    f.write "bar"
end

Dalam contoh itu, file ditutup secara otomatis.

Tonttu
sumber
Poin yang bagus. Saya melacak bug ke skrip yang tidak memanggil File.close. Akibatnya baris terakhir akan hilang di beberapa file sekarang dan nanti.
Erwan Legrand
Luar biasa. Saya tidak pernah tahu trik ini. Mirip seperti java-8 dalam hal itu. Terima kasih.
sagneta
2

Menurut http://ruby-doc.org/core-2.1.4/File.html#method-c-open

Tanpa blok terkait, File.open adalah sinonim untuk :: baru. Jika blok kode opsional diberikan, itu akan melewati file yang dibuka sebagai argumen dan objek File akan secara otomatis ditutup ketika blok berakhir. Nilai blok akan dikembalikan dari File.open.

Oleh karena itu, secara otomatis akan ditutup ketika blok berhenti : D

skozz
sumber
1
  1. Iya
  2. Jika tidak, atau jika ada kegagalan lainnya
  3. Lihat 2.
Satya
sumber
-3

Kita dapat menggunakan File.read()fungsi tersebut untuk membaca file di ruby ​​..... seperti,

file_variable = File.read("filename.txt")

dalam contoh ini file_variabledapat memiliki nilai penuh dari file itu ....

Kumar KS
sumber