何时需要向 `Thread.new` 传递参数?

18

似乎在线程之外定义的局部变量可以从内部访问,因此以下两个使用 Thread.new 的示例看起来是相同的:

a = :foo
Thread.new{puts a} # => :foo
Thread.new(a){|a| puts a} # => :foo

这个文档提供了以下示例:

arr = []
a, b, c = 1, 2, 3
Thread.new(a,b,c){|d, e, f| arr << d << e << f}.join
arr #=> [1, 2, 3]

但是,由于abc可以从创建的线程内部看到,所以这应该与以下代码相同:

arr = []
a, b, c = 1, 2, 3
Thread.new{d, e, f = a, b, c; arr << d << e << f}.join
arr #=> [1, 2, 3]

有什么区别吗?何时需要将本地变量作为参数传递给Thread.new

2个回答

25
当您将变量传递给线程时,线程会创建该变量的本地副本并使用它,因此对其进行的修改不会影响您传入的线程之外的变量。
a = "foo"
Thread.new{ a = "new"}
p a # => "new"
Thread.new(a){|d| d = "old"} 
p a # => "new"
p d # => undefined

1
如果我们在线程内覆盖d的值,那么将其作为参数传递的用例是什么?如果我们对d执行任何操作,则会反映在a的值中。 a = "foo" Thread.new { a = "new" } p a Thread.new(a) do |d| d.prepend("old") puts d # => "oldnew" puts a # => "oldnew" end - Kuldeep
当你说“本地副本”时,你是指使用#dup#clone创建浅拷贝还是深拷贝?还是深拷贝?我猜测是浅拷贝。因此它试图帮助良好的线程纪律,但只能做到这一步。如果Ruby Thread类文档在这个主题上更具体一些就好了。感谢清晰的示例和答案。 - pjvleeuwen
一个快速测试显示,在你的例子中,ad 仍然具有相同的 object_id。因此,只有变量是新的,但引用了相同的(非复制/克隆)对象。所以执行 d[1] = 'o' 将导致 a = 'now'a = 'new'; Thread.new(a) { |d| d[1] = 'o' }.join; puts a - pjvleeuwen

1
我认为我找到了实际问题。使用以下代码:

我认为我找到了实际问题。使用以下代码:

    sock = Socket.unix_server_socket(SOCK)
    sock.listen 10
    while conn = sock.accept do
        io, address = conn
        STDERR.puts "#{io.fileno}: Accepted connection from '#{address}'"
        Thread.new{ serve io }
    end

当接受少量连接时,它似乎能正常工作。但是当快速接受多个连接时,问题就出现了。除非将更新的本地变量io作为参数传递给Thread.new,否则将在多个并发线程中反映。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接