Dus*_*ser 8 ruby stack multithreading
假设我们有多个线程都调用相同的函数:
def foo
# do stuff ...
end
100.times do |i|
Thread.new do
foo
end
end
Run Code Online (Sandbox Code Playgroud)
如果当前有两个或多个线程foo,那么它们各自共享相同的局部变量foo吗?
这与我的第二个问题有关.线程是否具有单独的堆栈帧,或者它们是否在单个进程中共享堆栈帧?具体来说,当每个调用多个线程foo并foo返回之前,foo堆栈上是否有多个副本,每个副本都有自己的局部变量,或者foo堆栈中只有一个副本?
是的,它们共享相同的变量。这是线程的关键元素,在只读上下文中很好,但如果它们写入任何这些变量,则需要使用 aMutex和synchronize线程,因此在任何给定时间只有一个可以更改变量。有时他们可能会调用间接更改数据的方法,因此在决定是否需要同步之前,您需要完全了解系统。
至于你的第二个问题,如果我明白你在问什么,它们有单独的堆栈帧,但它们仍然在内存中共享相同的数据。
澄清一下,在下面的示例中,局部变量由多个线程zip 共享,因为它是在当前作用域中定义的(线程不会更改作用域,它们只是在当前作用域中启动一个单独的并行执行线程)。
zip = 42
t = Thread.new do
zip += 1
end
t.join
puts zip # => 43
Run Code Online (Sandbox Code Playgroud)
这里的连接拯救了我,但显然如果我将其保留在那里,那么线程中根本没有任何意义。如果我执行以下操作,将会很危险:
zip = 42
t = Thread.new do
zip += 1
end
zip += 1
puts zip # => either 43 or 44, who knows?
Run Code Online (Sandbox Code Playgroud)
那是因为基本上有两个线程都试图zip同时修改。当您访问网络资源或增加数字等时,这一点变得很明显,如上所述。
然而,在下面的示例中,局部变量zip是在一个全新的作用域内创建的,因此两个线程实际上并没有同时写入同一个变量:
def foo
zip = 42
zip += 1 # => 43, in both threads
end
Thread.new do
foo
end
foo
Run Code Online (Sandbox Code Playgroud)
有两个并行堆栈被管理,每个堆栈在foo方法内都有自己的局部变量。
然而,下面的代码是危险的:
@zip = 42 # somewhere else
def foo
@zip += 1
end
Thread.new do
foo
end
foo
puts @zip # => either 43 or 44, who knows?
Run Code Online (Sandbox Code Playgroud)
这是因为实例变量@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!
Run Code Online (Sandbox Code Playgroud)
如果当执行流到达该Mutex#synchronize行时,它会尝试锁定互斥体。如果成功,则进入该块并继续执行。一旦块完成,互斥体将再次解锁。如果互斥量已经被锁定,则线程会等待,直到它再次空闲......实际上,它就像一扇每次只有一个人可以穿过的门。
我希望这能解决问题。