File.open de Ruby e a necessidade de f.close

92

É de conhecimento comum na maioria das linguagens de programação que o fluxo para trabalhar com arquivos é abrir-usar-fechar. Ainda assim, eu vi muitas vezes em códigos Ruby chamadas File.open incomparáveis ​​e, além disso, encontrei esta joia de conhecimento nos documentos Ruby:

Os fluxos de E / S são fechados automaticamente quando são reivindicados pelo coletor de lixo.

darkredandyellow friendly irc aborda o problema:
[17:12] sim, e também, o número de descritores de arquivo geralmente é limitado pelo sistema operacional
[17:29] Presumo que você pode facilmente ficar sem descritores de arquivo disponíveis antes que o coletor de lixo limpe acima. nesse caso, você mesmo pode querer usar fechá-los. "reclamado pelo coletor de lixo." significa que o CG atua em algum momento no futuro. e é caro. uma série de razões para fechar arquivos explicitamente.

  1. Precisamos fechar explicitamente
  2. Se sim, por que o GC fecha automaticamente?
  3. Se não, por que a opção?
Clyfe
fonte
1
Seu 'conhecimento comum' está desatualizado desde que os destruidores foram inventados.
meagar
1
@meager: Quando os destruidores foram inventados?
Andrew Grimm
Apenas uma observação: embora os descritores de arquivo sejam limitados, pelo menos no Linux, o limite é bastante alto.
Linuxios
1
@Linuxios: no meu ubuntu12.04 $ ulimit -n => 1024só fica alto quando você faz apenas um trabalho simples. O mau hábito vai causar um grande problema um dia!
HVNSweeting

Respostas:

133

Eu vi muitas vezes em códigos Ruby File.openchamadas incomparáveis

Você pode dar um exemplo? Eu só vejo isso em código escrito por iniciantes que não têm "conhecimento comum na maioria das linguagens de programação de que o fluxo para trabalhar com arquivos é abrir-usar-fechar".

Rubistas experientes fecham explicitamente seus arquivos ou, de forma mais linguística, usam a forma de bloco de File.open, que fecha automaticamente o arquivo para você. Sua implementação é basicamente algo assim:

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

Os scripts são um caso especial. Os scripts geralmente são executados tão curtos e usam tão poucos descritores de arquivo que simplesmente não faz sentido fechá-los, pois o sistema operacional os fechará de qualquer maneira quando o script for encerrado.

Precisamos fechar explicitamente?

Sim.

Se sim, por que o GC fecha automaticamente?

Porque depois de coletar o objeto, não há mais como você fechar o arquivo e, portanto, você vazaria os descritores de arquivo.

Observe que não é o coletor de lixo que fecha os arquivos. O coletor de lixo simplesmente executa quaisquer finalizadores para um objeto antes de coletá-lo. Acontece que a Fileclasse define um finalizador que fecha o arquivo.

Se não, por que a opção?

Porque a memória desperdiçada é barata, mas os descritores de arquivo desperdiçados não. Portanto, não faz sentido vincular o tempo de vida de um descritor de arquivo ao tempo de vida de algum pedaço de memória.

Você simplesmente não pode prever quando o coletor de lixo será executado. Você não pode sequer prever se ele será executado em tudo : se você nunca fique sem memória, o coletor de lixo nunca vai ficar, portanto, o finalizador nunca funcionará, portanto, o arquivo nunca será fechado.

Jörg W Mittag
fonte
1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (embora seu Kernel # aberto e usado principalmente para o lado HTTP dele, mas eu cheguei a ele com um parâmetro de caminho de arquivo local, no entanto. ..; Ainda estou tentando encontrar tempo para corrigir o & request-pull), github.com/jnicklas/carrierwave Ctrl + f "File.open" (é dado como exemplo, mas de uma maneira ruim ...), e vários outros lugares que não me lembro. Estou
incomodado
3
Neste exemplo, o aumento deve estar dentro do bloco de resgate? Isso não irá apenas gerar um erro de tempo de execução se aumento for chamado e não houver exceção?
Jeff Storey
@JeffStorey: boa pegada! 17 meses despercebido…
Jörg W Mittag
@ JörgWMittag e agora mais 17 meses não corrigidos: PI acho que o ponto principal aqui é ensure, rescuee raisenão é necessário.
KL-7 de
Eu acho que você não pode ter ensuresem rescue. E você não pode simplesmente engolir a exceção silenciosamente, você precisa propagá-la para o chamador, após ter fechado o arquivo. De qualquer forma, lembre-me novamente em maio de '15 :-D
Jörg W Mittag
71

Você deve sempre fechar os descritores de arquivo após o uso, isso também irá liberá-los. Freqüentemente, as pessoas usam File.open ou método equivalente com blocos para lidar com o tempo de vida do descritor de arquivo. Por exemplo:

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

Nesse exemplo, o arquivo é fechado automaticamente.

Tonttu
fonte
Bom ponto. Eu rastreei um bug em um script que não chama File.close. Como resultado, a última linha estará faltando em alguns arquivos de vez em quando.
Erwan Legrand
Excepcional. Eu nunca conheci esse truque. Muito parecido com o java-8 nesse aspecto. Obrigado.
sagneta
2

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

Sem bloco associado, File.open é sinônimo de :: new. Se o bloco de código opcional for fornecido, ele receberá o arquivo aberto como um argumento e o objeto Arquivo será fechado automaticamente quando o bloco terminar. O valor do bloco será retornado de File.open.

Portanto, será fechado automaticamente quando o bloco terminar : D

Skozz
fonte
1
  1. sim
  2. Caso não o faça, ou se houver alguma outra falha
  3. Veja 2.
Satya
fonte
-3

Podemos usar a File.read()função para ler o arquivo em ruby ​​... como,

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

neste exemplo file_variablepode ter o valor total desse arquivo ....

Kumar KS
fonte