停止线程,确保运行某些最终代码

Phr*_*ogz 5 ruby console multithreading

对于这个答案,我编写了如下代码:

def show_wait_spinner
  dirty = false
  spinner = Thread.new{
    loop{
      print "*"
      dirty = true
      sleep 0.1
      print "\b"  
      dirty = false
    }    
  }
  yield
  spinner.kill
  print "\b" if dirty
end

print "A"
show_wait_spinner{ sleep rand }
puts "B"
Run Code Online (Sandbox Code Playgroud)

目的是确保最终输出为"AB"— "\b"如果线程尚未打印最终结果,则将其打印出来。在begin/rescue/ensure存在的Ruby中,该代码对我来说似乎很混乱。所以我尝试了其他的实现show_wait_spinner; 它们都无法确保"AB"始终是输出,从不"A*B"或"AB*"。

有没有更干净,更像Ruby的方式来实现此逻辑?

通过Mutex在循环结束时停止

def show_wait_spinner
  stop = false
  stopm = Mutex.new
  spinner = Thread.new{
    loop{
      print "*"
      sleep 0.1
      print "\b"
      stopm.synchronize{ break if stop }
    }    
  }
  yield
  stopm.synchronize{ stop = true }
  STDOUT.flush
end
Run Code Online (Sandbox Code Playgroud)

…但是我的逻辑必须关闭,因为这总是导致“ A * B”。

通过线程局部变量在循环结束时停止

第二次尝试导致有时打印“ A * B”,有时打印“ AB”:

def show_wait_spinner
  stop = false
  spinner = Thread.new{
    Thread.current[:stop] = false
    loop{
      print "*"
      sleep 0.1
      print "\b"
      stopm.synchronize{ break if Thread.current[:stop] }
    }    
  }
  yield
  spinner[:stop] = true
  STDOUT.flush
end
Run Code Online (Sandbox Code Playgroud)

杀死并确保线程

def show_wait_spinner
  spinner = Thread.new{
    dirty = false
    begin
      loop{
        print "*"
        dirty = true
        sleep 0.1
        print "\b"
        dirty = false
      }    
    ensure
      print "\b" if dirty
    end
  }
  yield
  spinner.kill
  STDOUT.flush
end
Run Code Online (Sandbox Code Playgroud)

提升和挽救线程

def show_wait_spinner
  spinner = Thread.new{
    dirty = false
    begin
      loop{
        print "*"
        dirty = true
        sleep 0.1
        print "\b"
        dirty = false
      }    
    rescue
      puts "YAY"
      print "\b" if dirty
    end
  }
  yield
  spinner.raise
  STDOUT.flush
end
Run Code Online (Sandbox Code Playgroud)

tad*_*man 5

为什么不杀死线程,为什么不翻转一个导致它在预定点停止的变量?如果让它循环通过并在循环结束时退出,您将没有那么多麻烦。

例如:

def show_wait_spinner
  running = true

  spinner = Thread.new do
    while (running) do
      print "*"
      sleep 0.1
      print "\b"  
    end
  end

  yield
  running = false
  spinner.join
end

print "A"
show_wait_spinner{ sleep rand }
puts "B"
Run Code Online (Sandbox Code Playgroud)

当您打电话时,Thread#kill您不知道线程在哪里,并且线程没有机会清理它在做什么。如果不遵守您的礼节性的“停止运行”请求,则始终可以终止线程。


mat*_*att 2

在互斥锁示例中,您需要等待线程完成才能退出该方法。当前您设置stop为 true,然后退出该方法,B在微调器线程能够唤醒并打印最后一个退格键以删除字符之前打印并结束脚本*。

另外,breakinstopm.synchronize{ break if stop }只退出内部块,而不退出循环,所以你需要使用catch/throw什么的。

但是,您不需要互斥体。这在 1.9.3 中对我有用:

def show_wait_spinner
  exit = false
  spinner = Thread.new{
    loop{
      print "*"
      sleep 0.1
      print "\b" 
      break if exit
    }    
  }
  yield
  exit = true
  spinner.join
end
Run Code Online (Sandbox Code Playgroud)

$stdout.sync = true在顶部添加使其可以在 1.8.7 中运行。