是的,它们共享同样的变量。这是线程的一个关键元素,在只读环境下没有问题,但如果它们写入任何这些变量,则需要使用互斥锁并使线程同步,以便在任何给定时间仅有一个线程可以更改变量。有时,它们可能会调用间接更改数据的方法,因此在决定是否需要同步之前,您需要完全了解系统。
至于您的第二个问题,如果我理解您的问题,它们具有单独的堆栈帧,但是它们仍然共享内存中的相同数据。
为澄清起见,在以下示例中,本地变量zip由多个线程共享,因为它在当前范围内定义(线程不会更改作用域,它们只是在当前范围内启动一个单独的并行执行线程)。
zip = 42
t = Thread.new do
zip += 1
end
t.join
puts zip
这里的连接救了我,但如果我保留它,显然线程就没有任何意义。如果我继续这样做,会很危险:
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
end
Thread.new do
foo
end
foo
有两个平行的堆栈被管理,每个堆栈都有自己的本地变量在foo
方法内。
但是下面的代码很危险:
@zip = 42
def foo
@zip += 1
end
Thread.new do
foo
end
foo
puts @zip
这是因为实例变量@zip
可以在foo
函数的范围之外访问,因此两个线程可能同时访问它。
这些“两个线程同时更改相同数据”的问题可通过在更改变量的代码段周围谨慎地放置互斥锁(锁)来解决。必须在创建线程之前创建互斥锁,因为在互斥锁的情况下,非常重要的一点是两个线程访问相同的互斥锁,以便知道它是否被锁定。
@mutex = Mutex.new
@zip = 42
def foo
@mutex.synchronize do
@foo += 1
end
end
Thread.new do
foo
end
foo
puts @zip
当执行流程到达 Mutex#synchronize
代码行时,会尝试锁定互斥锁。如果成功,它将进入块并继续执行。一旦块完成,互斥锁将再次解锁。如果互斥锁已经被锁定,线程将等待直到它再次变成自由状态......实际上这类似于一个只能同时通过一个人的门。
我希望这样能够解决您的问题。