Ruby Watir多线程控制同一浏览器实例报错及解决
解决Watir多线程操作浏览器时的read_nonblock/stream closed错误
我之前碰到过一模一样的问题:想在多线程里持续检查页面元素或操作Watir浏览器实例,结果频繁弹出read_nonblock和stream closed错误,网上搜了一圈也没找到明确的解释。后来才搞明白,这是因为多个线程同时访问同一个浏览器驱动实例,把底层的HTTP通信流给搞乱了——Watir/Selenium的浏览器对象本身就不是线程安全的,并发操作必然出问题。
复现问题的测试代码
下面这段代码会频繁触发错误,核心原因就是两个线程在没有任何同步机制的情况下,同时操作同一个browser对象:
require 'watir' require 'thread' profile = Selenium::WebDriver::Firefox::Profile.new profile['permissions.default.image'] = 2 profile['media.autoplay.enabled'] = false browser = Watir::Browser.new :firefox, profile: profile threads = Array.new threads[0] = Thread.new do loop do begin sleep(3) puts browser.url browser.goto("https://monip.org") rescue puts "thread 1 encountered an error" end end end threads[1] = Thread.new do loop do begin if browser.url == "https://monip.org" puts "hi" end rescue puts "thread 2 encountered an error" end end end threads.each do |e| e.join end
解决方案:用Mutex实现线程同步
既然浏览器实例不是线程安全的,那我们就得保证同一时间只有一个线程能操作它。Ruby的Mutex(互斥锁)刚好能解决这个问题——把所有访问浏览器的代码都包裹在mutex.synchronize块里,这样同一时间只有一个线程能执行这些代码,从根源上避免并发冲突。
修正后的代码如下:
require 'watir' require 'thread' profile = Selenium::WebDriver::Firefox::Profile.new profile['permissions.default.image'] = 2 profile['media.autoplay.enabled'] = false mutex = Mutex.new browser = Watir::Browser.new :firefox, profile: profile threads = Array.new threads[0] = Thread.new do loop do sleep(0.1) begin mutex.synchronize do puts "#{Time.now.ctime} #{browser.url}" browser.goto("https://monip.org") end rescue puts "#{Time.now.ctime} thread 1 encountered an error" end end end threads[1] = Thread.new do loop do sleep(0.1) begin mutex.synchronize do if browser.url == "https://monip.org/" || browser.url == "https://monip.org" puts "#{Time.now.ctime} hi" end end rescue puts "#{Time.now.ctime} thread 2 encountered an error" end end end threads[0].join threads[1].join
运行效果
修正后的程序可以稳定持续运行,再也不会出现之前的错误,典型输出示例:
Fri Jan 5 13:49:36 2018 about:blank Fri Jan 5 13:49:37 2018 hi ...
内容的提问来源于stack exchange,提问作者OmG3r
相关产品推荐
相关产品推荐

