多个线程调用相同的函数
假设我们有多个线程都调用相同的函数:
def foo # do stuff ... end 100.times do |i| Thread.new do foo end end
如果两个或多个线程当前在foo
,那么它们是否在foo
共享相同的局部变量?
这与我的第二个问题有关。 线程是否具有单独的堆栈帧,或者它们是否在单个进程中共享堆栈帧? 具体来说,当多个线程每个调用foo
并且在foo
返回之前,堆栈上是否有多个foo
副本,每个都有自己的局部变量,或者堆栈上只有一个foo
副本?
是的,他们共享相同的变量。 这是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
函数的范围之外访问,因此两个线程可能同时访问它。
“两个线程同时更改相同数据”的这些问题通过在代码中更改变量的部分周围使用仔细放置的互斥锁(锁)来解决。 必须在创建线程之前创建互斥锁,因为在互斥锁的情况下,(通过设计)两个线程访问相同的互斥锁是至关重要的,以便知道它是否被锁定。
# 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
行,它会尝试锁定互斥锁。 如果成功,它进入块并继续执行。 块完成后,互斥锁再次解锁。 如果互斥锁已被锁定,则线程会等待它再次自由…有效地它就像一扇门,一次只能有一个人走过。
我希望这可以解决问题。
在方法内定义的局部变量不共享。 但是,如果线程在线程块的范围内,则线程可以访问同一对象的实例变量。
例如:
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。