赛璐珞令人讨厌的竞争条件

时间:2014-09-30 22:27:44

标签: ruby celluloid

我有一个脚本可以生成用户指定数量的IP地址,并尝试在某个端口上连接所有这些IP地址。我使用Celluloid和这个脚本来提供合理的速度,因为同步扫描2000个主机可能需要很长时间。但是,我告诉脚本扫描2000个随机主机。我发现它实际上只扫描了大约一半的数字。如果我告诉它扫描3000,我得到相同的基本结果。如果我做1000或更少,它似乎工作得更好,但即使我只扫描1000个主机,它通常最终只有920相对一致性。我意识到生成随机IP地址显然会失败一些,但我发现很难相信每次都有大约70个不正确生成的IP地址。所以这里是代码:

class Scan
include Celluloid

def initialize(arg1)
    @arg1 = arg1
    @host_arr = []
    @timeout = 1
end


def popen(host)
    addr = Socket.getaddrinfo(host, nil)
    sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)

    begin
        sock.connect_nonblock(Socket.pack_sockaddr_in(22, addr[0][3]))

    rescue Errno::EINPROGRESS
        resp = IO.select(nil, [sock], nil, @timeout.to_i)

        if resp.nil?
            puts "#{host}:Firewalled"
        end

        begin
            if sock.connect_nonblock(Socket.pack_sockaddr_in(22, addr[0][3]))
               puts "#{host}:Connected"
            end

        rescue Errno::ECONNREFUSED
            puts "#{host}:Refused"
          rescue
              false
        end
    end
    sock
end


def asynchronous
    s = 1
    threads = []

        while s <= @arg1.to_i do
            @host_arr << Array.new(4){rand(254)}.join('.')
            s += 1
        end

        @host_arr.each do |ip|
            threads << Thread.new do
                begin
                    popen(ip)
                rescue
                end
            end
        end

        threads.each do |thread|
            thread.join
        end
end

end

scan = Scan.pool(size: 100, args: [ARGV[0]])

(0..20).to_a.map { scan.future.asynchronous }

大约一半的时间我得到了这个:

  

D,[2014-09-30T17:06:12.810856#30077] DEBUG - :终止11名演员......    W,[2014-09-30T17:06:12.812151#30077]警告 - :终止任务:type =:finalizer,meta = {:method_name =&gt;: shutdown },状态=:接收       Celluloid :: TaskFiber回溯不可用。如果您需要回溯,请尝试Celluloid.task_class = Celluloid::TaskThread

并且脚本什么都不做。剩下的时间(只有当我指定超过1000时)我得到这个:http://pastebin.com/wTmtPmc8

所以,我的问题是这个。我如何避免竞争条件和死锁,同时仍然在这个特定的脚本中实现我想要的东西?

1 个答案:

答案 0 :(得分:2)

自己启动低级线程会干扰Celluloid的功能。而是创建一个扫描对象池并立即为它们提供所有IP。他们将排队等候可用的

class Scan
   def popen
     …
   end
end

scanner_pool = Scan.pool(50)
resulsts = @host_arr.map { |host| scanner_pool.scan(host) }