假设我们有多个线程都调用相同的函数:
def foo
# do stuff ...
end
100.times do |i|
Thread.new do
foo
end
end
如果foo
内有两个或多个线程,那么它们是否在foo
内共享相同的局部变量?
这与我的第二个问题有关。线程是否具有单独的堆栈帧,或者它们是否在单个进程内共享堆栈帧?具体来说,当多个线程各自调用foo
并且foo
返回之前,堆栈上是否存在foo
的多个副本,每个副本都有自己的局部变量,或者只有一个副本{堆栈上有{1}}吗?
答案 0 :(得分:4)
是的,他们共享相同的变量。这是Threads的一个关键元素,在只读上下文中很好,但是如果它们写入任何这些变量,则需要使用Mutex
和synchronize
个线程,所以只有一个在任何给定时间更改变量。有时他们可能会调用间接更改数据的方法,因此您需要在决定是否需要同步之前完全了解系统。
至于你的第二个问题,如果我理解你在问什么,他们有单独的堆栈帧,但他们仍然在内存中共享相同的数据。
澄清,在以下示例中,局部变量zip
由多个线程共享,因为它是在当前范围内定义的(线程不会更改范围,它们只是在当前范围内启动一个单独的并行执行线程。)
zip = 42
t = Thread.new do
zip += 1
end
t.join
puts zip # => 43
这里的联接拯救了我,但显然线程中没有任何意义,如果我保留那里。如果我要做以下事情将是危险的:
zip = 42
t = Thread.new do
zip += 1
end
zip += 1
puts zip # => either 43 or 44, who knows?
那是因为你基本上有两个线程都试图同时修改zip
。当您访问网络资源或增加数字等时,这会变得很明显,如上所述。
但是,在下面的示例中,局部变量zip
是在一个全新的范围内创建的,因此两个线程实际上并不是同时写入同一个变量:
def foo
zip = 42
zip += 1 # => 43, in both threads
end
Thread.new do
foo
end
foo
有两个并行堆栈被管理,每个堆栈在foo
方法中都有自己的局部变量。
但是,以下代码很危险:
@zip = 42 # somewhere else
def foo
@zip += 1
end
Thread.new do
foo
end
foo
puts @zip # => either 43 or 44, who knows?
这是因为实例变量@zip
可以在foo
函数的范围之外访问,因此两个线程可能同时访问它。
“两个线程同时更改相同数据”的这些问题通过在代码中更改变量的部分周围使用仔细放置的互斥锁(锁)来解决。在创建线程之前必须创建Mutex ,因为在Mutex的情况下,两个线程按顺序访问相同的Mutex(按设计)是至关重要的知道它是否被锁定。
# somewhere else...
@mutex = Mutex.new
@zip = 42
def foo
@mutex.synchronize do
@foo += 1
end
end
Thread.new do
foo
end
foo
puts @zip # => 44, for sure!
如果执行流程到达Mutex#synchronize
行,它会尝试锁定互斥锁。如果成功,它进入块并继续执行。块完成后,互斥锁将再次解锁。如果互斥锁已被锁定,则线程会等待它再次自由...有效地它就像一扇门,一次只能有一个人走过。
我希望这能解决问题。
答案 1 :(得分:0)
不共享在方法内定义的局部变量。但是,如果线程在线程块的范围内,则线程可以访问同一对象的实例变量。
例如:
def foobar
puts "Foo is defined!" if defined?(foo)=='local-variable'
foo = 5
end
如果被多个线程调用,永远不会放置字符串。
但是以下需要同步的互斥锁,因为竞争条件适用:
foo = {bar:5}
def foobar(value)
value[:bar]+=5
end
15.times{|i| Thread.new{foobar foo}}
在此之后,foo [:bar]可能包含值35,因为每次调用foobar都会更改散列内的值foo。