如果我在单独的Ruby线程中进行密集计算,为什么Ruby1.9GUI会挂起?
Ruby 1.9应该有本机线程,如果一些线程输入本机代码(如GUI工具包主循环或某些Ruby库的C实现),GIL应该提升 但如果我开始遵循在主线程中显示GUI的简单代码示例,并在单独的线程中进行一些基本的数学运算,GUI将挂起,请尝试调整窗口大小,以便自己查看。我已经使用不同的GUI工具包Qt(qtbindings gem)进行了检查——它的行为完全相同。在Windows7和OSX10.7上使用Ruby1.9.3-p0进行测试如果我在单独的Ruby线程中进行密集计算,为什么Ruby1.9GUI会挂起?,ruby,multithreading,ruby-1.9,Ruby,Multithreading,Ruby 1.9,Ruby 1.9应该有本机线程,如果一些线程输入本机代码(如GUI工具包主循环或某些Ruby库的C实现),GIL应该提升 但如果我开始遵循在主线程中显示GUI的简单代码示例,并在单独的线程中进行一些基本的数学运算,GUI将挂起,请尝试调整窗口大小,以便自己查看。我已经使用不同的GUI工具包Qt(qtbindings gem)进行了检查——它的行为完全相同。在Windows7和OSX10.7上使用Ruby1.9.3-p0进行测试 require 'tk' require 'thread' Thre
require 'tk'
require 'thread'
Thread.new { loop { a = 1 } }
TkRoot.new.mainloop()
Python中的相同代码在没有任何GUI挂起的情况下运行良好:
from Tkinter import *
from threading import *
class WorkThread( Thread ) :
def run( self ) :
while True :
a = 1
WorkThread().start()
Tk().mainloop()
我做错了什么
更新
看起来Ubuntu linux上哪里没有这样的问题,所以我的问题主要是关于Windows和OSX
更新
有人指出OSX上哪里没有这样的问题。因此,我制定了一个分步指南,以隔离和重现一个问题:
test.rb
文件并运行它。尝试调整窗口大小-您将看到严重的延迟。从代码中删除线程,开始并尝试调整窗口大小-延迟消失。我录了一段./configure --with-arch=x86_64,i386 --enable-pthread --enable-shared --with-gcc=clang --prefix=/usr
make
sudo make install
你的线程块将使用100%个CPU,这是不可能的任何真正的代码会吃那么多(如果你正在做真正的密集计算,你应该考虑另一种语言),也许尝试添加一些暂停:
require 'tk'
require 'thread'
require 'rexml/document'
Thread.new { loop { sleep 0.1; a = 1 } }
TkRoot.new.mainloop()
你的代码在Mac OS X 10.7和1.9.3 btw上运行良好
虽然我非常喜欢ruby,但在我看来,当前的gui库状态非常糟糕,因此我避免使用它。这种挂起可能是由工具箱中ruby绑定的C代码造成的。正如您所知,ruby线程有一个:the。TKC线程和纯Ruby线程之间的混合似乎并不顺利 对于类似的情况,您可以尝试在
要求“tk”之前添加这些行。
:
module TkCore
RUN_EVENTLOOP_ON_MAIN_THREAD = true
end
图形工具包需要一个主线程来刷新图形元素。如果您的线程处于密集计算中,那么您的线程会大量请求锁,因此会干扰toolkit的线程
如果愿意,您可以避免使用睡眠技巧。在Ruby 1.9中,可以使用或。根据oldmoe的说法,纤维
如果可以使用,还可以保留Ruby线程。这就是ruby 1.9.3中并行测试的方式。这似乎是解决Ruby线程和GIL限制的好方法
文档显示了一个示例用法:
rd, wr = IO.pipe
if fork
wr.close
puts "Parent got: <#{rd.read}>"
rd.close
Process.wait
else
rd.close
puts "Sending message to parent"
wr.write "Hi Dad"
wr.close
end
rd,wr=IO.pipe
如果叉子
关闭
放入“父对象得到:”
道口
等等
其他的
道口
将“发送消息给家长”
写“嗨,爸爸”
关闭
结束
fork
调用启动两个进程。在内部,如果,则您处于父进程中。在else
中,您是孩子。对Process.wait的调用关闭子进程。
例如,您可以尝试在主gui循环中读取您的子对象,只有在收到所有数据后才关闭并等待子对象
编辑:如果选择在Windows下使用fork(),则需要执行以下操作。根据平台的不同,您可以设置线程的优先级:
require 'tk'
require 'thread'
require 'rexml/document'
t1 = Thread.new { loop { a = 1 } }
t1.priority = 0
t2 = TkRoot.new.mainloop()
t2.priority = 100
如果你认真地使用多线程,你可能想考虑使用JRuBuy。它使用Java线程实现Ruby线程,使您能够访问Java并发库、工具和经过良好测试的代码
在大多数情况下,您只需将ruby命令替换为jruby命令
这里有一个开始。
不确定,但Ruby 1.9是否仍然拥有GIL(全局解释器锁)?这完全可以解释你的问题…@Romain GIL如何解释我的问题?Python有相同的GIL,没有问题。GIL意味着只有一个线程可以同时运行ruby代码,所以如果您的后台计算可以使用它,您的UI代码就不能使用。@Romain Python有相同的GIL,没有这样的问题。Ruby调度程序将在一段时间后停止后台线程(大约100条Ruby指令?),并给其他线程一些CPU时间。通过快速切换,Ruby将实现近乎并行的执行。例如,如果您启动两个都运行ruby代码的ruby线程,ruby将在线程之间快速切换,因此对您来说,它们将像并行一样执行。这很有意义。不管怎样,你都看不出你的代码有什么问题。由于它不能是GIL…在python代码中,它也有100%的CPU负载,但没有GUI冻结。多线程处理就是执行多个线程,而不管每个线程的CPU使用情况如何。例如,如果您使用相同的无限循环启动两个ruby线程,那么这两个线程将完全并行工作,每个线程使用50%的CPU。那么为什么GUI不是这样工作的呢?关于OSX 10.7-只需使用resize而不是window move,OSX将无延迟地移动窗口,即使GUI没有响应,这是操作系统的特性。我认为发生的事情是GUI主循环是在C中的,因为您的ruby代码占用了大量cpu。应用程序在ruby端比在C端花费更多的时间。我从来没有用过tk,但这种现象应该接近事实。(我不知道它在python上是如何工作的,也不知道为什么它在ruby上的工作方式不同,也许库的架构不同)AFAIK ruby和python使用相同的tcl/tk库。不管怎样,我所知道的任何工具包的情况都是一样的:Tk、Qt、GTK等等。所有这些在python中都可以正常工作,在Ruby中GUI严重滞后。我的问题是“为什么会这样”:。我不是说tk库本身,而是说wra