いくつかのスレッドを保持するクラスを作成し、タスクを実行し、最後にコールバック メソッドを呼び出すことが私の現在の目標であり、この道では特別なことは何もありません。
私の実験的なクラスは、特定の IP の特定のポートでいくつかの接続チェックを行い、ステータス情報を提供します。
だから私の試み:
check = ConnectionChecker.new do | threads |
# i am done callback
end
check.check_connectivity(ip0, port0, timeout0, identifier0)
check.check_connectivity(ip1, port1, timeout1, identifier1)
check.check_connectivity(ip2, port2, timeout2, identifier2)
sleep while not check.is_done
最善のアプローチではないかもしれませんが、一般的には私の場合に適しています。
それで何が起こっているのですか:
私のクラスでは、コールバックを保存し、アクションを実行し、内部的なことを行います:
Thread.new
-> 成功/失敗 -> すべて完了したら完了としてマーク -> コールバックを呼び出す:
class ConnectionChecker
attr_reader :is_done
def initialize(&callback)
@callback = callback
@thread_count = 0
@threads = []
@is_done = false
end
def check_connectivity(host, port, timeout, ident)
@thread_count += 1
@threads << Thread.new do
status = false
pid = Process.spawn("nc -z #{host} #{port} >/dev/null")
begin
Timeout.timeout(timeout) do
Process.wait(pid)
status = true
end
rescue Process::TimeoutError => e
Process.kill('TERM', pid)
end
mark_as_done
#returnvalue for the callback.
[status, ident]
end
end
# one less to go..
def mark_as_done
@thread_count -= 1
if @thread_count.zero?
@is_done = true
@callback.call(@threads)
end
end
end
このコード - はい、開始メソッドがないことはわかっているので、すべてをすぐに呼び出すことを信頼する必要があります - 正常に動作します。
しかし、これらの2行を交換すると:
@is_done = true
@callback.call(@threads)
に
@callback.call(@threads)
@is_done = true
それから最後の行、
sleep while not check.is_done
無限ループになる。デバッグすると、コールバックが適切に呼び出されていることがわかります。 の値を確認すると、is_done
実際には常に ですfalse
。クロージャーに入れていないので、なぜこれが起こっているのだろうか。
コールバック自体も空のis_done
ままにすることができますfalse
(そのため、誤ってキャッチされた例外はありません)。
この場合、最後のスレッドのステータスが実行中であることに気付きました。私はスレッドの値を尋ねなかったので、ここでこつがわかりません。
この問題に関するドキュメント/情報はありますか? また、それの名前は問題ありません。