3

PHP クラス内では、パーサーは__constructおよび__destructメソッドを処理してインスタンスをインスタンス化し、スクリプトが終了するか unset を使用するとインスタンスを破棄します。クラスを拡張するときは、 および を使用して、拡張されたクラスで実行する必要があるクリーンアップ コードを実行するだけparent::__constructですparent::__destruct

DBデータを表し、そのデータを操作するのに役立つクラスのコンテキスト内で、__destructメソッドを使用して現在の値をDBから取得した元の値と比較し、必要に応じてUPDATEを実行できると考えていました(場合によっては常に常に)主キーの値を変更できない限り、UPDATE を実行します)。これを PHP に実装するのは非常に簡単です。

このアプローチの主な利点は、クラス変数を必要に応じてすばやく操作し、最後にクラスに 1 つの大きな UPDATE を実行させることです。数分間実行される長いスクリプトでは、__construct 中に DB インスタンスを作成し、データを取得して DB 接続を閉じ、数分間の実行中にのみクラス変数を操作するとよいでしょう。__destruct で、新しい DB 接続を開き、UPDATE を作成してから、DB 接続を閉じて、クリーンアップが必要なものをすべてクリーンアップします。

これが良いアイデア/悪い習慣であるかどうかについて、人々の考えがどうなっているのか興味がありますが、私の主な質問は、Rubyでこれが可能かということでした.

Ruby には、クラスのインスタンスをインスタンス化するときに実行される initialize メソッドがあります。parent::__constructRuby のisに相当するものはsuperRuby にあります。そして、Ruby クラスのObjectSpace.define_finalizeandメソッドがあります。finalizeただし、私が理解しているように、 finalize メソッドは、それを呼び出しているインスタンスを参照できないはずです。その上、に相当するものが見つかりませんparent::__destructfinalizeメソッド自体の参照を防ぐように明示的に設計されているように見えるため、同等のものがないためだと思います。

これを行う方法を知っている人はいますか?そうでない場合、Ruby クラスをダンプしてリソースを取り戻し、データの損失を防ぐためのベスト プラクティスは何ですか? クラスインスタンスを nil に設定する直前に呼び出すガベージコレクションメソッドを誰もが持っていますか、それとも他の方法がありますか?

ありがとう

4

2 に答える 2

4

いいえ、Ruby には PHP__destructに相当するものはありません。なぜなら、PHP は参照カウントがゼロになるとすぐにオブジェクトを破棄しますが、Ruby はオブジェクトを破棄するのが遅いからです。Ruby は時々オブジェクトをマークしてスイープします。また、Ruby は C コードのローカル変数をスキャンする際に保守的です。C ローカルがオブジェクトを指している可能性がある場合、Ruby はオブジェクトの破棄を拒否します。

RubyはObjectSpace.define_finalizerあるけど使いにくい。オブジェクトは複数のファイナライザーを持つことができます。スーパークラスがファイナライザーを定義した場合、同じオブジェクトに別のファイナライザーを定義できます。問題はObjectSpace.undefine_finalizer、オブジェクトからすべてのファイナライザーを削除することです。したがって、スーパークラスがそのファイナライザーを削除すると、ファイナライザーも削除されます。

Ruby は、オブジェクトを破棄した後にのみファイナライザーを実行します。ファイナライザーには、オブジェクトへの参照があってはなりません。その場合、Ruby はオブジェクトを破棄せず、メモリ リークが発生します。selfファイナライザーは、またはローカル変数がオブジェクトを参照するスコープ内にあってはなりません。

次の例では、ファイナライザーを使用してデータベースを更新する方法を示します。Ruby はファイナライザーの実行が遅いため、これは悪い考えです。データベースが長時間更新されない可能性があります。GC.start完全なガベージ コレクションを強制するために呼び出します。私のコンピューターの Ruby はオブジェクトの 1 つを破棄することを拒否するため、1 つの更新はまだ行われていません。

require 'forwardable'
require 'pstore'

# Create a database of people's ages.
People = PStore.new('people.pstore')
People.transaction do
  People['Alice'] = 20
  People['Bob'] = 30
  People['Carl'] = 40
  People['David'] = 50
  People['Eve'] = 60
end

# Shows people in database.  This can show old values if someone
# forgot to update the database!
def show_people(heading)
  People.transaction(true) do
    puts heading
    %w[Alice Bob Carl David Eve].each do |name|
      puts "  #{name} is #{People[name]} years old."
    end
  end
end

show_people("Before birthdays:")

# This is a person in the database.  You can change his or her age,
# but the database is only updated when you call #update or by this
# object's finalizer.
class Person
  # We need a PersonStruct for the finalizer, because Ruby destroys
  # the Person before calling the finalizer.
  PersonStruct = Struct.new(:name, :age, :oage)
  class PersonStruct
    def update(_)
      s = self
      if s.age != s.oage
        People.transaction { People[s.name] = s.oage = s.age }
      end
    end
  end
  private_constant :PersonStruct

  # Delegate name (r) and age (rw) to the PersonStruct.
  extend Forwardable
  def_delegators(:@struct, :name, :age, :age=)

  # Find a person in the database.
  def initialize(name)
    age = People.transaction(true) { People[name] }
    @struct = PersonStruct.new(name, age, age)
    ObjectSpace.define_finalizer(self, @struct.method(:update))
  end

  # Update this person in the database.
  def update
    @struct.update(nil)
  end
end

# Now give everyone some birthdays.
Person.new('Alice').age += 2
Person.new('Bob').age += 1
Person.new('Carl').age += 1
Person.new('David').age += 1
Person.new('Eve').age += 2

# I forgot to keep references to the Person objects and call
# Person#update.  Now I try to run the finalizers.
GC.start

# Did the database get updated?
show_people("After birthdays:")

私のコンピューターは次の出力を出します:

Before birthdays:
  Alice is 20 years old.
  Bob is 30 years old.
  Carl is 40 years old.
  David is 50 years old.
  Eve is 60 years old.
After birthdays:
  Alice is 22 years old.
  Bob is 31 years old.
  Carl is 41 years old.
  David is 51 years old.
  Eve is 60 years old.

イブの年齢を2歳足しましたが、データベースを確認するまで更新はありませんでした。Ruby を解釈する C コードはPerson.new('Eve')、一部のローカル変数に参照を残している可能性があるため、Ruby はオブジェクトを破棄しません。別のバージョンの Ruby または別の C コンパイラを使用すると、結果が変わる可能性があります。Ruby はプログラムの終了時に残りのファイナライザーを実行するため、更新は行われましたが、遅すぎました。

于 2017-09-17T03:41:38.090 に答える
3

pstが彼のコメントで述べたように、ルビーのデストラクタは必要ありません。すべての参照変数をnull(ref = nil)に設定するだけで、オブジェクトはガベージコレクションによって削除されます。そのgarbaceがいつ収集(削除)されたかを正確に知ることはできません。さらに、そのオブジェクトを実際に削除する前に実行するprocを作成することもできます(私はお勧めしません)。

ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc)
于 2012-08-21T17:41:03.843 に答える